Terms and concepts.
A natural transformation is a mapping between functors that preserves the structure of the underlying categories. — Bartosz Milewski from Understanding Yoneda
This is a case where definitions are confusing, but the actual thing is quite simple in practice. Let's try that out in Scala using the Functors List
and Option
.
_17val someString: Option[String] = Some("foo")_17val noneString: Option[String] = None_17_17// We can intuitively think of Option as being a List of 0 or 1 elements, so the_17// natural transformation is trivial (note: I'm intentionally avoiding the use_17// of Scala's own .toList):_17def optionToList[A](a: Option[A]): List[A] =_17 a.map(x => List(x)).getOrElse(List.empty[A])_17_17optionToList(someString)_17//=> res: List[String] = List(foo)_17_17optionToList(noneString)_17//=> res: List[String] = List()_17_17optionToList(None)_17//=> res: List[Nothing] = List()
In mathematics, an isomorphism (from the Greek: isos "equal", and morphe "shape") is a homomorphism (or more generally a morphism) that admits an inverse. Two mathematical objects are isomorphic if an isomorphism exists between them. — Wikipedia
Now the Scala in which we'll describe the world's most trivial isomorphism:
_17// These two objects are isomorphic because a morphism (i.e. function) exists_17// that maps each to the other._17val nameAge = ("foo", 42)_17val ageName = (42, "foo")_17_17def tuple2Iso[A, B](p: (A, B)): (B, A) = (p._2, p._1)_17_17// A => B_17tuple2Iso(nameAge) == ageName_17//=> true_17_17// B => A_17tuple2Iso(ageName) == nameAge_17//=> true_17_17tuple2Iso(tuple2Iso(nameAge)) == nameAge_17//=> true
Domains come from set theory, and represent the set of input values for a function, while codomains represent the set of output values. Therefore, a function is the mapping between its domain and codomain.
In programming, types and domains are related but not quite the same. Domains are strictly related to functions, while a type specifies a set of values.
Read Existential type on the HaskellWiki.
Existential types provide a way of baking the generics into a type instead of explicitly declaring them. Consider the ultra-contrived example where we have a type Things
which contains a list of stuff whose type we don't care about because the only operation we want to perform on it is to count how many there are.
_10case class Things[A](list: List[A])_10val intThings: Things[Int] = Things(List(1, 2, 3))_10def count[A](ts: Things[A]) = ts.list.size_10count(intThings)_10//=> 3
An example based on Higher-Rank Polymorphism in Scala:
_10def r1[A](f: A => A, a: A): A = f(a)_10r1({ i: Int => i * i }, 10)_10// res4: Int = 100
Again from Higher-Rank Polymorphism in Scala:
_10trait ~>[F[_],G[_]] {_10 def apply[A](a: F[A]): G[A]_10}
captures the intuition that all instances of a polymorphic function act the same way — Wikipedia
A function which is not defined for some inputs.
A function which is defined for all inputs, as opposed to a partial function.
In logic, extensionality, or extensional equality, refers to principles that judge objects to be equal if they have the same external properties. It stands in contrast to the concept of intensionality, which is concerned with whether the internal definitions of objects are the same. — Wikipedia
_12f :: Int -> Int_12f x = x + x + x_12_12g :: Int -> Int_12g x = x * 3_12_12-- f and g are extensionally equal but not intensionally equal._12_12h :: Int -> Int_12h x = x + x + x_12_12-- f and h are intensionally equal.