
Scala's Option monad versus null-conditional operator in C# - miloszpp
http://www.codewithstyle.info/2016/02/scalas-option-monad-versus-null.html
======
louthy
C# has native support for monads (LINQ), so it's trivial to implement an
Option monad.

Or you can go the whole hog like I have my language-ext project [1][2].

In C# it would look like this:

    
    
        var city = from author  in article.author
                   from address in author.address
                   from city    in address.city
                   select city.ToLower();
    

Which is arguably even more elegant than the Scala 'for' notation.

As an aside, there was a discussion [3] on one of the language-ext issues
about my Try<T> implementation, and I was asked to compare it to Scala's Try
monad. The crux of it was that it's perfectly possible to capture the majority
of the use-cases, but may be of interest in the context of this discussion.

[1] [https://github.com/louthy/language-
ext](https://github.com/louthy/language-ext)

[2] [https://github.com/louthy/language-
ext/blob/master/LanguageE...](https://github.com/louthy/language-
ext/blob/master/LanguageExt.Core/Option.cs)

[3] [https://github.com/louthy/language-
ext/issues/33#issuecommen...](https://github.com/louthy/language-
ext/issues/33#issuecomment-146557934)

~~~
airless_bar
Implementing a monad is only 30% of the deal.

Being able to abstract over monads (and other type constructors) ... that's
the part that really matters.

C# is not expressive enough for that.

~~~
louthy
It doesn't do higher-kinded polymorphism, no. If you're referring to something
else, please elaborate on what it can't express. But for the example given in
the blog piece it's exactly as expressive and concise - so it's not giving C#
a fair hearing.

It _is_ actually possible to get most of the way to HKT using extension
methods. But it involves a hell of a lot of typing. In my project I used a
template generator to do it. It's still pretty limited though, and there isn't
a single type-class called Monad.

~~~
airless_bar
> It doesn't do higher-kinded polymorphism, no.

That's what I referred to.

> ...

The point is that you can't leverage all the existing
monad/functor/applicative/free/yoneda... libraries out there–they don't exist,
because they can't be written in a way that would allow re-use.

~~~
daxfohl
Just out of curiosity what kind of reusable monad libraries are out there for
Scala/Haskell? Given the scope of everything between Maybe and IO, I've never
understood what one could make that would be useful to "monads" in general
(beyond the sugar for do-notation).

~~~
runT1ME
[https://github.com/scalaz/scalaz](https://github.com/scalaz/scalaz)

Monad Transformers for one. Traverse/Sequence are another super common
(amazing) pattern that requires abstracting over types.

[http://eed3si9n.com/learning-scalaz-day12](http://eed3si9n.com/learning-
scalaz-day12)

is a nice tutorial series on it.

~~~
daxfohl
Ah, light bulb moment (perhaps).

So in F# I've been using specialized asyncSeq and asyncMaybe libraries. With
monad transformers (and thus HKT's), would those come largely for free?

And you could traverse a list<maybe<list<_>> combo like [Some [Some [1; 2];
None; Some [3]]; None; Some [None; Some [4]]] with no more difficulty than a
regular list-of-lists?

~~~
runT1ME
Exactly!

You can also write a generic "sequence" over a list, for instance, so that any
'monad' (task/future, maybe/option, state, either/disjunction etc.) can 'swap
places'.

So your List<Future<A>> can become Future<List<A>>. But you only need to write
it _once_. Then it doesn't matter what the inner type of the List is, as long
as it's a monad (applicative, really, but all monads are applicatives).

~~~
daxfohl
Crazy, I'd always considered monad transformers a sore thumb on, well, all
that stuff. The fact that monads weren't enough but then you had to go
reproduce all that stuff in monad _transformers_ as well. The whole thing just
reeked of code duplication and bad smells so I didn't really look into it
further.

Now looking at the Scalaz impl, the transformers are actually the core thing,
and monads themselves are just transformers over Identity. So it all fits and
isn't redundant. Certainly changes a lot of perspective.

~~~
daxfohl
That said, I'd still put LINQ as 99% of "the deal" per the original comment.
The extra abstractions allowed by HKT's, while "necessary" mathematically,
still only save me about 1% of the work required to implement asyncSeq and
asyncMaybe, and in 15 years of pro experience those are the only real uses of
HKT's I've ever had the need for.

------
hibikir
As usual when someone tries to get into explaining monads, mistakes happen.

First, on the scala standard library and monads: Scala very carefully avoided
creating a Monad trait, on purpose. So while List and Options are monads, we
can't just go and say that everything that has a flatmap is a monad.

Also, the reason we can combine options and lists in a for comprehension is
not really that they both have a flatmap: Try doing that with Future instead,
and it will not work quite so easily! The reason those two have such easy
interoperability is that in Scala's Predef there is a conversion that turns an
Option into a Traversable, which is being called under the covers. For
comprehensions are very powerful, but switching from one monadic context to
another without help isn't one of them. In the case of Future and List, for
instance, we'd have to use some of the utility methods in the Future object
that can help transform a future into a List[Future], and a List[Future] into
Future[List]

Don't get me wrong, it's great to see people trying to explain this kind of
stuff, but it is so very hard to cover the topic in a way that is both
approachable and that doesn't leave people with intuitions that will fail them
outside of the given example.

~~~
miloszpp
[Author] Thanks for your remarks.

I agree on the first point. As to the second, I didn't mean that having
flatmap is the reason why we can combine Options and Lists. I meant that
Options are composable with other Options and Lists are composable with other
Lists.

I understand the problem of working with different monads. I looked into monad
transformers at some point and found them difficult to work with so my
approach now is to avoid mixing monad types (for example instead of having
Future[Option[T]], just use a failed Future instead of None).

------
VoiceOfWisdom
The same can be accomplished in F# for those who want this power in the .net
ecosystem

    
    
        type Address = { street : string; city : string option }
        type Author = { name : string; email : string; address : Address option }
        type Article = { title : string; content : string; author : Author option }
    
        let printCity (article : Article) =
            article.author
            |> Option.bind(fun author -> author.address)
            |> Option.bind(fun address -> address.city)
            |> Option.iter(fun city -> printfn "%s" city)

~~~
daxfohl
Better to do with the maybe workflow from Fsharpx:

    
    
        maybe {
          let! address = author.address
          let! city = address.city
          printfn %s city
        }

------
krcz
Scala version can be written as one-liner as well; losing readability, but it
might be ok for fast hacking / prototyping:

article.author.flatMap(_.address).flatMap(_.city).map(_.toLowerCase).foreach(println)

And in for-comprehension version there is no need to use yield, as we don't
need result of println.

~~~
dk8996
We work with Scala at our company and this is the style we use. I think that
it's something that even a new person to Scala can easy understand.

~~~
Filligree
Hell, I've never used Scala at all, and I can read that.

~~~
closetnerd
You can tell that "_.author" is a parameterless lambda?

~~~
Filligree
Um, no? It takes one parameter.

Wouldn't fit into the map type signature otherwise.

------
jameslk
Why not just use less dots to begin with? Or, in other words, apply the Law of
Demeter[0]. To me, diving so many levels deep into dependencies is a code
smell because it tightly couples code to sub-dependencies and makes
refactoring more difficult.

0\.
[https://en.wikipedia.org/wiki/Law_of_Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter)

~~~
daxfohl
This is important for typical OO chaining

i.e. you should avoid:

serviceX.getServiceY().getServiceZ().getSomeValue();

But for just structured data it's not constructive to write accessor wrappers
at each level. And for things like recursive data structures, it's not even
possible.

------
paulddraper
The differences go far beyond syntax.

Option/Maybe can represent _arbitrary_ optionality, while null is limited to a
sentinel value.

I wrote a blog post about null
([https://www.lucidchart.com/techblog/2015/08/31/the-worst-
mis...](https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-
computer-science/)) and this one of the examples:

Suppose you have a generic key-value store. If a value is not present, the
lookup returns null. You use it for a cache that maps user IDs to phone
numbers. Some people don't have phone numbers, i.e. their phone number is
null. But now you can't represent that in your cache. (JS "fixes" this -- if
you want to call it that -- by using a second type of null: undefined.)

Option is generic and can applied arbitrarily deep; null/Nullable is a
sentinel that can be used once.

Option is arbitrarily composable, and the superior choice.

------
CuriousSkeptic
The null-conditional operator is a language feature. The Option monad is a
class in the standard library.

You can have Option in C# and use it with linq to get more or less the same
feature and syntax. There are a few implementations on nuget if you don't want
to write your own.

------
qwtel
In case anybody is interested, this is how you would do this in clojure:

    
    
        (println (some-> article :author :address :city upper-case))

~~~
sandisk5
(some-> article :author :address :city upper-case println)

So that you don't print nil if some-> returns nil.

------
gmantom
Why not just use F#, it's better than Scala and C# at these sorts of things
and functional first and immutable first.

~~~
runT1ME
It's really not. I worked for a .NET shop that migrated some new projects to
Scala. We evaluated F#, but decided the power of Scala was worth the platform
switch. F# lacks higher kinds and implicit parameters, meaning you can't
really use the type class pattern. This hurt our productivity.

~~~
azth
> F# lacks higher kinds and implicit parameters, meaning you can't really use
> the type class pattern. This hurt our productivity.

Genuinely curious, but what are you doing that the lack of type classes hurts
productivity so much?

~~~
runT1ME
It's just annoying to have to re-implement sequence/traverse and every
combination of monad stacks you want to for each type. It's doable, but bug
prone and frustrating.

I now work for one of the largest scala teams in the US. We make heavy use of
the Scalaz project and regularly use monad transormers or free monads and both
require higher kinded types.

------
draw_down
I think this is the first time I've ever _really_ understood what the hell
flatMap is for. So, thanks!

~~~
miloszpp
[Author] That's awesome to hear that, thanks :)

------
merb
Actually in scala for just getting the city and lowercase it I would write
that: article.author.flatMap(_.address.flatMap(_.city.map(_.toLowerCase)))

I know that's by far not as good looking as the c# version, however at the end
all comes down to syntactic sugar.

------
joaoqalves
tl; dr: Scala Option monad is more elegant :)

------
jeroen

      Console.WriteLine(article?.Author?.Address?.City?.ToUpper());
    

That is actually a bit different than the first piece of code, since it calls
Console.WriteLine.

------
wantreprenr007
Much better than the usual terrible patterns of pyramids of doom.

Another reason to set tab stops to 4+ characters: make PoDs impractical and
better code legible.

