Hi guys! What do you think about languages with ta...
# announcements
w
Hi guys! What do you think about languages with tagged unions aka sum types? Did you managed to use such a language? Kotlin has something that reminds me them: sealed classes, but they look very verbose and I am not sure if they are useful as tagged unions… (I have to check this, my Kotlin experience is too short)
s
witoldsz: care to share a good example?
r
I’ve used them in SML and scala. They are very elegant in SML and very useful. When I started looking at Kotlin I was hoping it was a port of SML to the JVM.
w
So, I have used tagged unions in Elm. It was my first contact with this, let me show an example of application structure: A top level model of the application looks like this:
Copy code
type alias Model =
    { page : Page
    , csrfToken : String
    }

type Page
    = NoPage
    | AnnouncementListPage (WebData (List Announcement))
    | AnnouncementItemPage (WebData AnnouncementForm)
    | PayoutCancelledListPage PayoutCancelledListForm
    | PayoutListPage PayoutListForm
WebData
comes from a library, it looks like this:
Copy code
type RemoteData e a
    = NotAsked
    | Loading
    | Failure e
    | Success a

type alias WebData a =
    RemoteData Http.Error a
reference: http://package.elm-lang.org/packages/ohanhi/remotedata-http/latest
In such a small amount of code one can see the application through! My favorite features is that such a type system literally does not allow invalid state of an app to be modeled. This brings lots of positive consequences…
p
modern oo languages achieve a similar effect with sealed classes or fancy enums. kotlin’s sealed classes are more verbose than sum types, but that’s life.
👍 1
s
is the difference mainly just verbosity?
w
I do not know yet…
as far as I can tell, the
WebData
could not be modeled with
enum
, because enums are all singletons.
NotAsked
and
Loading
would work, but
Failure …
and
Success a
are tagged values.
s
if there is variable data use a sealed class heierachy, if it’s static/no data use enum
w
Success "Hello from Backend"
produces an instance of
RemoteData
.
In Kotlin
Success
would be a constructor with one argument, but in the same time
Loading
is… just a
Loading
. Each
Loading
is equal to any other one.
s
would still be equal in kotlin in terms of value, but not instance.
although you could use an object if you wanted only one of the data-less versions (although I don’t see why unless allocating millions or something)
w
So, it looks like sealed classes are just a verbose version of tagged unions?
Now, when I think about it, sealed class should be able to map 1:1, shouldn't it?
It is, BTW, such a nice construct to model a property with a value fetched from remote location, isn't it?
I am thinking how could
WebData
alias (in Kotlin) represent a special version of
RemoteData
sealed class…
🤔 1
For not familiar with Elm's syntax:
WebData e a
,
e
stands for any error,
a
stands for, well anything (successful response). The
WebData
is a RemoteData with
e
error of type
Http.Error
and
a
is left as-is.
my first attempt looks like this:
Copy code
sealed class RemoteData<E,A>
object notAsked : RemoteData<Any,Any>()
object loading : RemoteData<Any,Any>()
data class Failure<Any>(val error: Any) : RemoteData<Any,Any>()
data class Success(val success: Any) : RemoteData<Any,Any>()
Does not looks OK thogh, the
<E,A>
cannot be used in
Failure
and
Success
, compiler complains it does not know what E and A are.
second attempt looks promising:
Copy code
sealed class RemoteData<E,A>
object notAsked : RemoteData<Any,Any>()
object loading : RemoteData<Any,Any>()
data class Failure<E>(val error: E) : RemoteData<E,Any>()
data class Success<A>(val success: A) : RemoteData<Any,A>()

class HttpError {/*whatever*/}

typealias WebData<A> = RemoteData<HttpError, A>
I am not sure it it's working, it just compiles…
p
I was mentioning enums because some oo langs (e.g. swift) have enums that are more powerful than kotlin’s (each enum value can carry different data). also heard that java folks are considering to go in that direction.
w
I guess the
Copy code
object notAsked : RemoteData<Any,Any>()
object loading : RemoteData<Any,Any>()
data class Failure<E>(val error: E) : RemoteData<E,Any>()
data class Success<A>(val success: A) : RemoteData<Any,A>()
Should be:
Copy code
object notAsked : RemoteData<Nothing,Nothing>()
object loading : RemoteData<Nothing,Nothing>()
data class Failure<E>(val error: E) : RemoteData<E,Nothing>()
data class Success<A>(val success: A) : RemoteData<Nothing,A>()
It's even more to write, though 😕
c
@witoldsz The cleanest modern language supporting sum and product types is Ceylon in my opinion, take a look at it
w
I really started to like Kotlin 🙂 Will look at Ceylon anyway.
At first look, Ceylon does not look attractive… I wish there was something like F# for JVM so it could coop with Java like Kotlin.
👍🏽 1
r
@witoldsz Scala models them similar to Kotlin as you may already know and they are getting improved syntax in Dotty. A more important feature for Kotlin that would make sum types and other FP techniques more appealing would be Higher Kinded types. Without these is hard to do any serious stuff in typed FP in Kotlin and emulating them is a real pain. https://github.com/kategory/kategory/blob/master/katz/src/main/kotlin/katz/typeclasses/HigherKinds.kt#L3
Another one is typeclasses. I'd take those over better syntax for sum types any time
w
If you have a free minute to spare, I would strongly advise reading this little post: https://becoming-functional.com/higher-level-coding-with-elm-union-types-2502d1f5a615
This guy has expressed my whole point of view at that matter.
d
Swift models these more concisely as it's parameterised 'enum' implementation.
Kotlins sealed classes are a little more verbose, but still invaluable, say, when representing variously branched states of a state machine.
r
Nice blog, it is doable in Kotlin, but it is way more verbose. I’ve been adopting that technique, but what drove me to that was not “pretty code” but weird errors at runtime when integrating with external services. Modelling those interactions as state machines virtually eliminated those kinds of errors because the clients of the code were forced to think about what to do for each “valid” state and not just ignore it.