Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Standard name of a sum type like Either but for 3 cases?

Is there a standard sum type like Either but for 3 cases? Haskell has These but it's not quite that.

like image 498
Trident D'Gao Avatar asked Aug 07 '16 12:08

Trident D'Gao


People also ask

How do you use sum() with a case when?

To start, it selects the column department from the table subject. Then comes the curious use of a SUM () with a CASE WHEN. This expression says whenever the number_of_lectures is higher than 20, the row is assigned the value 1. If the condition is not met, the assigned value is 0.

What is sum type in C++ with example?

For example, the sum type of a boolean and an unsigned integer ( uint+bool) represents exactly one value in the set {true, false, 0, 1, .., 4294967295}. This type can be written in C++ using the following syntax, which we call C-style unions:

What is the best language to define sum types?

It’s trivial to define sum types in languages like Haskell, Scala, Rust, Swift, OCaml, F#, Kotlin because algebraic data types are the core concept of these languages by their design. The most expressive languages are Haskell, OCaml, and F# — they offer the most laconic syntax of an ADT definition.

Is there a way to approximate the sum of two types?

Well, really, all you can do is run different code depending on whether the Maybe is a Nothing or Just. This is the visitor pattern, which is another way to approximate sum types in languages without them. Visitor has the right maintainability characteristics (easy to add uses, hard to add cases), but it involves a great deal of boilerplate.


3 Answers

These are called co-products really an Either is simply a 2 argument co-product. You can use helpers from the shapeless library to build arbitrary length co-products using:

type CP = Int :+: String :+: Boolean :+: CNil

val example = Coproduct[CP]("foo")

You can then use all the fun poly magic to map them or perform other operations:

object printer extends Poly1 {
  implicit def caseInt = at[Int](i => i -> s"$i is an int")
  implicit def caseString = at[String](s => s -> s"$s is a string")
  implicit def caseBoolean = at[Boolean](b => s -> s"$b is a bool")
}
val mapped = example map printer
mapped.select[(String, String)] shouldEqual "foo is a string"

Scala.JS + Shapeless can work together as far as I know, so that may give you what you want.

like image 140
flavian Avatar answered Oct 19 '22 16:10

flavian


In recent Haskell, I'd switch on a bit of kitchen sink.

{-# LANGUAGE PolyKinds, DataKinds, GADTs, KindSignatures,
    TypeOperators, PatternSynonyms #-}

Then I'd define type-level list membership

data (:>) :: [x] -> x -> * where
  Ze ::            (x ': xs) :> x
  Su :: xs :> x -> (y ': xs) :> x

and now I have all the finite sums, without cranking out a whole raft of OneOfN type definitions:

data Sum :: [*] -> * where
  (:-) :: xs :> x -> x -> Sum xs

But, to address Tomas's issue about readability, I'd make use of pattern synonyms. Indeed, this sort of thing is the reason I've been banging on about pattern synonyms for years.

You can have a funny version of Maybe:

type MAYBE x = Sum '[(), x]

pattern NOTHING :: MAYBE x
pattern NOTHING = Ze :- ()

pattern JUST :: x -> MAYBE x
pattern JUST x = Su Ze :- x

and you can even use newtype to build recursive sums.

newtype Tm x = Tm (Sum '[x, (Tm x, Tm x), Tm (Maybe x)])

pattern VAR :: x -> Tm x
pattern VAR x = Tm (Ze :- x)

pattern APP :: Tm x -> Tm x -> Tm x
pattern APP f s = Tm (Su Ze :- (f, s))

pattern LAM :: Tm (Maybe x) -> Tm x
pattern LAM b = Tm (Su (Su Ze) :- b)

The newtype wrapper also lets you make instance declaration for types built that way.

You can, of course, also use pattern synonyms to hide an iterated Either nicely.

This technique is not exclusive to sums: you can do it for products, too, and that's pretty much what happens in de Vries and Löh's Generics-SOP library.

The big win from such an encoding is that the description of data is itself (type-level) data, allowing you to cook up lots of deriving-style functionality without hacking the compiler.

In the future (if I have my way), all datatypes will be defined, not declared, with datatype descriptions made of data specifiying both the algebraic structure (allowing generic equipment to be computed) of the data and its appearance (so you can see what you're doing when working with a specific type).

But the future is sort of here already.

like image 33
pigworker Avatar answered Oct 19 '22 15:10

pigworker


I think that heavily relying on type like this is an anti-pattern.

One of the nicest things you get from using algebraic data types is that the resulting type tells you something about the domain that you are working with. With a generic type like Choice<T1, T2, T3>, you are really not saying anything about the domain.

I think option<T> (aka Maybe) is quite clear in that it says that a value of type T is either there or it is missing for some reason. I think Either<'T, exn> is still quite clear in that it says you get a value or an exception. However when you have more than two cases, it becomes quite hard to understand what is meant by the cases and so explicitly defining a type with names to match the domain might be a good idea.

(I do use Choice<T1, T2, T3> in F# occasionally, but the usage is typically limited to a small scope - less than 50 lines of code - so that I can easily find what the meaning is in the surroundings of the code that consumes it.)

like image 43
Tomas Petricek Avatar answered Oct 19 '22 15:10

Tomas Petricek