Skip to content

Instantly share code, notes, and snippets.

@friedbrice
Created January 15, 2017 04:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save friedbrice/559c344fd2dfd8fd51b3eebbfbb88fba to your computer and use it in GitHub Desktop.
Save friedbrice/559c344fd2dfd8fd51b3eebbfbb88fba to your computer and use it in GitHub Desktop.
Some ADTs in Scala.
sealed trait Either[S, T] {
def fold[X]: (S => X) => (T => X) => X
def map[X]: (T => X) => Either[S, X]
def bind[X]: (T => Either[S, X]) => Either[S, X]
}
object Either {
def Left[S, T]: S => Either[S, T] =
s => new Either[S, T] {
def fold[X]: (S => X) => (T => X) => X =
f => _ => f(s)
def map[X]: (T => X) => Either[S, X] =
_ => Either.Left[S, X](s)
def bind[X]: (T => Either[S, X]) => Either[S, X] =
_ => Either.Left[S, X](s)
}
def Right[S, T]: T => Either[S, T] =
t => new Either[S, T] {
def fold[X]: (S => X) => (T => X) => X =
_ => f => f(t)
def map[X]: (T => X) => Either[S, X] =
f => Either.Right[S, X](f(t))
def bind[X]: (T => Either[S, X]) => Either[S, X] =
k => k(t)
}
}
sealed trait Pair[S, T] {
def fold[X]: (S => T => X) => X
def map[X]: (T => X) => Pair[S, X]
def bind[X]: (T => Pair[S, X]) => Pair[S, X]
}
object Pair {
def apply[S, T]: S => T => Pair[S, T] =
s => t => new Pair[S, T] {
def fold[X]: (S => T => X) => X =
f => f(s)(t)
def map[X]: (T => X) => Pair[S, X] =
f => Pair(s)(f(t))
def bind[X]: (T => Pair[S, X]) => Pair[S, X] =
k => k(t)
}
}
sealed trait Maybe[T] {
def fold[X]: X => (T => X) => X
def map[X]: (T => X) => Maybe[X]
def bind[X]: (T => Maybe[X]) => Maybe[X]
}
object Maybe {
def Nothing[T]: Maybe[T] =
new Maybe[T] {
def fold[X]: X => (T => X) => X =
x => _ => x
def map[X]: (T => X) => Maybe[X] =
_ => Maybe.Nothing
def bind[X]: (T => Maybe[X]) => Maybe[X] =
_ => Maybe.Nothing
}
def Just[T]: T => Maybe[T] =
t => new Maybe[T] {
def fold[X]: X => (T => X) => X =
_ => f => f(t)
def map[X]: (T => X) => Maybe[X] =
f => Maybe.Just(f(t))
def bind[X]: (T => Maybe[X]) => Maybe[X] =
k => k(t)
}
}
sealed trait List[T] {
def fold[X]: (T => X => X) => X => X
def map[X]: (T => X) => List[X]
def bind[X]: (T => List[X]) => List[X]
}
object List {
def Nil[T]: List[T]
= new List[T] {
def fold[X]: (T => X => X) => X => X =
_ => x => x
def map[X]: (T => X) => List[X] =
_ => List.Nil
def bind[X]: (T => List[X]) => List[X] =
_ => List.Nil
}
def Cons[T]: T => List[T] => List[T] =
t => ts => new List[T] {
def fold[X]: (T => X => X) => X => X =
g => x => g(t)(ts.fold(g)(x))
def map[X]: (T => X) => List[X] =
f => List.Cons(f(t))(ts.map(f))
def bind[X]: (T => List[X]) => List[X] =
k => k(t).fold(List.Cons)(ts.bind(k))
}
}
@friedbrice
Copy link
Author

friedbrice commented Jan 15, 2017

Written in the trait/object idiom followed by Argonaut and by Scrooge. By "trait/object idiom" i mean that (1) there is no (public) class defining instances of the datatype, (2) the trait specifies extractors, and (3) the object specifies constructors. Additionally in these examples, the trait defines no fields, only methods, so the resulting instances are immutable.

@friedbrice
Copy link
Author

Left to do: (1) meditate on whether the arguments to the various constructors and eliminators should be lazy, (2) meditate on whether standard tools such as equality checks, to string methods, read-from-string methods, and the likes (all the things you derive in Haskell) belong here or belong in another object.

@friedbrice
Copy link
Author

friedbrice commented Jan 15, 2017

The constructors and extractors are analogous to the introduction rules and elimination rules of formal logics. I mean "analogous" in the strongest possible sense, i.e., they are the same, modulo a recasting of terminology.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment