How is "a monoid on applicative functors" different than "a monoid in the category of endofunctors"? - monads

Perhaps neither of these statements are categorically precise, but a monad is often defined as "a monoid in the category of endofunctors"; a Haskell Alternative is defined as "a monoid on applicative functors", where an applicative functor is a "strong lax monoidal functor". Now these two definitions sound pretty similar to the ignorant (me), but work out significantly differently. The neutral element for alternative has type f a and is thus "empty", and for monad has type a -> m a and thus has the sense "non-empty"; the operation for alternative has type f a -> f a -> f a, and the operation for monad has type (a -> f b) -> (b -> f c) -> (a -> f c). It seems to me that the real important detail is in the category of endofunctors versus over endofunctors, though perhaps the "strong lax" detail in alternative is important; but that's where I get confused because within Haskell at least, monads end up being alternatives: and I see that I do not yet have a precise categorical understanding of all the details here.
How can it be precisely expresseed what the difference is between alternative and monad, such that they are both monoids relating to endofunctors, and yet the one has an "empty" neutral and the other has a "non-empty" neutral element?

In general, a monoid is defined in a monoidal category, which is a category that defines some kind of (tensor) product of objects and a unit object.
Most importantly, the category of types is monoidal: the product of types a and b is just a type of pairs (a, b), and the unit type is ().
A monoid is then defined as an object m with two morphisms:
eta :: () -> m
mu :: (m, m) -> m
Notice that eta just picks an element of m, so it's equivalent to mempty, and curried mu becomes mappend of the usual Haskell Monoid class.
So that's a category of types and functions, but there is also a separate category of endofunctors and natural transformations. It's also a monoidal category. A tensor product of two functors is defined as their composition Compose f g, and unit is the identity functor Id. A monoid in that category is a monad. As before we pick an object m, but now it's an endofunctor; and two morphism, which now are natural transformations:
eta :: Id ~> m
mu :: Compose m m ~> m
In components, these two natural transformations become:
return :: a -> m a
join :: m (m a) -> m a
An applicative functor may also be defined as a monoid in the functor category, but with a more sophisticated tensor product called Day convolution. Or, equivalently, it can be defined as a functor that (laxly) preserves monoidal structure.
Alternative is a family of monoids in the category of types (not endofunctors). This family is generated by an applicative functor f. For every type a we have a monoid whose mempty is an element of f a and whose mappend maps pairs of f a to elements of f a. These polymorphic functions are called empty and <|>.
In particular, empty must be a polymorphic value, meaning one value per every type a. This is, for instance, possible for the list functor, where an empty list is polymorphic in a, or for Maybe with the polymorphic value Nothing. Notice that these are all polymorphic data types that have a constructor that doesn't depend on the type parameter. The intuition is that, if you think of a functor as a container, this constructor creates and empty container. An empty container is automatically polymorphic.

Both concepts are tied to the idea of a "monoidal category", which is a category you can define the concept of a monoid in (and certain other kinds of algebraic structures). You can think of monoidal categories as: a category defines an abstract notion of functions of one argument; a monoidal category defines an abstract notion of functions of zero arguments or multiple arguments.
A monad is a monoid in the category of endofunctors; in other words, it's a monoid where the product (a function of 2 arguments) and the identity (a function of 0 arguments) use the concept of multi-argument function defined by a particular (bizarre) monoidal category (the monoidal category of endofunctors and composition).
An applicative functor is a monoidal functor. In other words, it's a functor that preserves all the structure of a monoidal category, not just the part that makes it a category. It should be obvious that that means it has mapN functions for functions with any number of arguments, not just functions of one argument (like a normal functor has).
So a monad exists within a particular monoidal category (which happens to be a category of endofunctors), while an applicative functor maps between two monoidal categories (which happen to be the same category, hence it's a kind of endofunctor).

To supplement the other answers with some Haskell code, here is how we might represent the Day convolution monoidal structure #Bartosz Milewski refers to:
data Day f g a = forall x y. Day (x -> y -> a) (f x) (g y)
With the unit object being the functor Identity.
Then we can reformulate the applicative class as a monoid object with respect to this monoidal structure:
type f ~> g = forall x. f x -> g x
class Functor f => Applicative' f
where
dappend :: Day f f ~> f
dempty :: Identity ~> f
You might notice how this rhymes with other familiar monoid objects, such as:
class Functor f => Monad f
where
join :: Compose f f ~> f
return :: Identity ~> f
or:
class Monoid m
where
mappend :: (,) m m -> m
mempty :: () -> m
With some squinting, you might also be able to see how dappend is just a wrapped version of liftA2, and likewise dempty of pure.

Related

Point-free at top-level in Standard ML

The first and the second flatMap work well. Why doesn't the third one work?
fun flatMap f xs = List.concat(List.map f xs)
fun flatMap f = List.concat o List.map f
val flatMap = (fn mmp => List.concat o mmp) o List.map;
This is due to a rule called "value polymorphism" or the "value restriction". According to this rule, a value declaration can't create a polymorphic binding if the expression might be "expansive"; that is, a value declaration can only create a polymorphic binding if it conforms to a highly restricted grammar that ensures it can't create ref cells or exception names.
In your example, since (fn mmp => List.concat o mmp) o List.map calls the function o, it's not non-expansive; you know that o doesn't create ref cells or exception names, but the grammar can't distinguish that.
So the declaration val flatMap = (fn mmp => List.concat o mmp) o List.map is still allowed, but it can't create a polymorphic binding: it has to give flatMap a monomorphic type, such as (int -> real list) -> int list -> real list. (Note: not all implementations of Standard ML can infer the desired type in all contexts, so you may need to add an explicit type hint.)
This restriction exists to ensure that we don't implicitly cast from one type to another by writing to a ref cell using one type and reading from it using a different type, or by wrapping one type in a polymorphic exception constructor and unwrapping it using a different type. For example, the below programs are forbidden by the value restriction, but if they were allowed, each would create a variable named garbage of type string that is initialized from the integer 17:
val refCell : 'a option ref =
ref NONE
val () = refCell := SOME 17
val garbage : string =
valOf (! refCell)
val (wrap : 'a -> exn, unwrap : exn -> 'a) =
let
exception EXN of 'a
in
(fn x => EXN x, fn EXN x => x)
end
val garbage : string =
unwrap (wrap 17)
For more information:
"ValueRestriction" in the MLton documentation
"Value restriction" on the English Wikipedia
"Types and Type Checking" in SML/NJ's guide to converting programs from Standard ML '90 to Standard ML '97. (Standard ML '90 had a different version of this rule, that was more permissive — it would have allowed your program — but considered "somewhat subtle" and in some cases "unpleasant", hence its replacement in Standard ML '97.)
the following sections of The Definition of Standard ML (Revised) (PDF):
§4.7 "Non-expansive Expressions", page 21, which defines which expressions are considered "non-expansive" (and can therefore be used in polymorphic value declarations).
§4.8 "Closure", pages 21–22, which defines the operation that makes a binding polymorphic; this operation enforces the value restriction by preventing the binding from becoming polymorphic if the expression might be expansive.
inference rule (15), page 26, which uses the aforementioned operation; see also the comment on page 27.
the comment on inference rule (20), page 27, which explains why the aforementioned operation is not applied to exception declarations. (Technically this is somewhat separate from the value restriction; but the value restriction would be useless without this.)
§G.4 "Value Polymorphism", pages 105–106, which discusses this change from Standard ML '90.

Do OCaml 'underscore types' (e.g. '_a) introduce the possibility of runtime type errors / soundness violations?

I was reading a little bit about the value restriction in Standard ML and tried translating the example to OCaml to see what it would do. It seems like OCaml produces these types in contexts where SML would reject a program due to the value restriction. I've also seen them in other contexts like empty hash tables that haven't been "specialized" to a particular type yet.
http://mlton.org/ValueRestriction
Here's an example of a rejected program in SML:
val r: 'a option ref = ref NONE
val r1: string option ref = r
val r2: int option ref = r
val () = r1 := SOME "foo"
val v: int = valOf (!r2)
If you enter the first line verbatim into the SML of New Jersey repl you get
the following error:
- val r: 'a option ref = ref NONE;
stdIn:1.6-1.33 Error: explicit type variable cannot be generalized at its binding declaration: 'a
If you leave off the explicit type annotation you get
- val r = ref NONE
stdIn:1.6-1.18 Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)
val r = ref NONE : ?.X1 option ref
What exactly is this dummy type? It seems like it's completely inaccessible and fails to unify with anything
- r := SOME 5;
stdIn:1.2-1.13 Error: operator and operand don't agree [overload conflict]
operator domain: ?.X1 option ref * ?.X1 option
operand: ?.X1 option ref * [int ty] option
in expression:
r := SOME 5
In OCaml, by contrast, the dummy type variable is accessible and unifies with the first thing it can.
# let r : 'a option ref = ref None;;
val r : '_a option ref = {contents = None}
# r := Some 5;;
- : unit = ()
# r ;;
- : int option ref = {contents = Some 5}
This is sort of confusing and raises a few questions.
1) Could a conforming SML implementation choose to make the "dummy" type above accessible?
2) How does OCaml preserve soundness without the value restriction? Does it make weaker guarantees than SML does?
3) The type '_a option ref seems less polymorphic than 'a option ref. Why isn't let r : 'a option ref = ref None;; (with an explicit annotation) rejected in OCaml?
Weakly polymorphic types (the '_-style types) are a programming convenience rather than a true extension of the type system.
2) How does OCaml preserve soundness without the value restriction? Does it make weaker guarantees than SML does?
OCaml does not sacrifice value restriction, it rather implements a heuristic that saves you from systematically annotating the type of values like ref None whose type is only “weekly” polymorphic. This heuristic by looking at the current “compilation unit”: if it can determine the actual type for a weekly polymorphic type, then everything works as if the initial declaration had the appropriate type annotation, otherwise the compilation unit is rejected with the message:
Error: The type of this expression, '_a option ref,
contains type variables that cannot be generalized
3) The type '_a option ref seems less polymorphic than 'a option ref. Why isn't let r : 'a option ref = ref None;; (with an explicit annotation) rejected in OCaml?
This is because '_a is not a “real” type, for instance it is forbidden to write a signature explicitly defining values of this “type”:
# module A : sig val table : '_a option ref end = struct let option = ref None end;;
Characters 27-30:
module A : sig val table : '_a option ref end = struct let option = ref None end;;
^^^
Error: The type variable name '_a is not allowed in programs
It is possible to avoid using these weakly polymorphic types by using recursive declarations to pack together the weakly polymorphic variable declaration and the later function usage that completes the type definition, e.g.:
# let rec r = ref None and set x = r := Some(x + 1);;
val r : int option ref = {contents = None}
val set : int -> unit = <fun>
1) Could a conforming SML implementation choose to make the "dummy" type above accessible?
The revised Definition (SML97) doesn't specify that there be a "dummy" type; all it formally specifies is that the val can't introduce a polymorphic type variable, since the right-hand-side expression isn't a non-expansive expression. (There are also some comments about type variables not leaking into the top level, but as Andreas Rossberg points out in his Defects in the Revised Definition of Standard ML, those comments are really about undetermined types rather than the type variables that appear in the definition's formalism, so they can't really be taken as part of the requirements.)
In practice, I think there are four approaches that implementations take:
some implementations reject the affected declarations during type-checking, and force the programmer to specify a monomorphic type.
some implementations, such as MLton, prevent generalization, but defer unification, so that the appropriate monomorphic type can become clear later in the program.
SML/NJ, as you've seen, issues a warning and instantiates a dummy type that cannot subsequently be unified with any other type.
I think I've heard that some implementation defaults to int? I'm not sure.
All of these options are presumably permitted and apparently sound, though the "defer unification" approach does require care to ensure that the type doesn't unify with an as-yet-ungenerated type name (especially a type name from inside a functor, since then the monomorphic type may correspond to different types in different applications of the functor, which would of course have the same sorts of problems as a regular polymorphic type).
2) How does OCaml preserve soundness without the value restriction? Does it make weaker guarantees than SML does?
I'm not very familiar with OCaml, but from what you write, it sounds like it uses the same approach as MLton; so, it should not have to sacrifice soundness.
(By the way, despite what you imply, OCaml does have the value restriction. There are some differences between the value restriction in OCaml and the one in SML, but none of your code-snippets relates to those differences. Your code snippets just demonstrate some differences in how the restriction is enforced in OCaml vs. one implementation of SML.)
3) The type '_a option ref seems less polymorphic than 'a option ref. Why isn't let r : 'a option ref = ref None;; (with an explicit annotation) rejected in OCaml?
Again, I'm not very familiar with OCaml, but — yeah, that seems like a mistake to me!
To answer the second part of your last question,
3) [...] Why isn't let r : 'a option ref = ref None;; (with an explicit annotation) rejected in OCaml?
That is because OCaml has a different interpretation of type variables occurring in type annotations: it interprets them as existentially quantified, not universally quantified. That is, a type annotation only has to be right for some possible instantiation of its variables, not for all. For example, even
let n : 'a = 5
is totally valid in OCaml. Arguably, this is rather misleading and not the best design choice.
To enforce polymorphism in OCaml, you have to write something like
let n : 'a. 'a = 5
which would indeed cause an error. However, this introduces a local quantifiers, so is still somewhat different from SML, and doesn't work for examples where 'a needs to be bound elsewhere, e.g. the following:
fun pair (x : 'a) (y : 'a) = (x, y)
In OCaml, you have to rewrite this to
let pair : 'a. 'a -> 'a -> 'a * 'a = fun x y -> (x, y)

Does compare work for all types?

Let's consider a type t and two variables x,y of type t.
Will the call compare x y be valid for any type t? I couldn't find any counterexample.
The polymorphic compare function works by recursively exploring the structure of values, providing an ad-hoc total ordering on OCaml values, used to define structural equality tested by the polymorphic = operator.
It is, by design, not defined on functions and closures, as observed by #antron. The recursive nature of the definition implies that structural equality is not defined on values containing a function or a closure. This recursive nature also imply that the compare function is not defined on recursive values, as mentioned by a #antron as well.
Structural equality, and therefore the compare function and the comparison operators, is not aware of structure invariants and cannot be used to compare (mildly) advanced data structures such as Sets, Maps, HashTbls and so on. If comparison of these structures is desired, a specialised function has to be written, this is why Set and Map define such a function.
When defining your own structures, a good rule of thumb is to distinguish between
concrete types, which are defined only in terms of primitive types and other concrete types. Concrete types should not be used for structures whose processing expects some invariants, because it is easy to create arbitrary values of this type breaking these invariants. For these types, the polymorphic comparison function and operators are appropriate.
abstract types, whose concrete definition is hidden. For these types, it is best to provide specialised comparison function. The mixture library defines a compare mixin that can be used to derive comparison operators from the implementation of a specialised compare function. Its use is illustrated in the README.
It doesn't work for function types:
# compare (fun x -> x) (fun x -> x);;
Exception: Invalid_argument "equal: functional value".
Likewise, it won't (generally) work for other types whose values can contain functions:
# type t = A | B of (int -> int);;
type t = A | B of (int -> int)
# compare A A;;
- : int = 0
# compare (B (fun x -> x)) A;;
- : int = 1
# compare (B (fun x -> x)) (B (fun x -> x));;
Exception: Invalid_argument "equal: functional value".
It also doesn't (generally) work for recursive values:
# type t = {self : t};;
type t = { self : t; }
# let rec v = {self = v};;
val v : t = {self = <cycle>}
# let rec v' = {self = v'};;
val v' : t = {self = <cycle>}
# compare v v;;
- : int = 0
# compare v v';;
(* Does not terminate. *)
These cases are also listed in the documentation for compare in Pervasives.

Types of OCaml functions

I started learning functional programming (OCaml), but I don't understand one important topic about functional programming: types.
Can anyone explain me this solution please?
Have a test this week and can't get reach the resolution..
let f a b c = a (a b c) 0;;
f: ('a -> int -> 'a) -> 'a -> int -> 'a
let f a b c = a (a b c) 0;;
Your confusion involves types and type inference, i.e., when you define a function or binding, you don't need to give explicit types for its parameters, nor the function/binding itself, OCaml will figure it out if your definition of function/binding is correct.
So, let's do some manual inferences ourselves. If a human can do, then the compiler can also do.
1.
let x = 1
1 is integer, so x must be an integer. So you don't need to do int x = 1 as in other languages, right?
2.
let f x = 1
If there are multiple variable names between let and =, then it must be a function definition, right? Otherwise, it won't make sense. In Java like language, it also makes no sense to say int x y = 1, right?
So f is a function and x is must be a parameter. Since the righthand side of = is an integer, then we know f will return an integer. For x, we don't know, so x will be thought as a polymorphic type 'a.
So f: 'a -> int = <fun>.
3.
let f a b c = a (a b c) 0;;
f is a function, with parameters a, b, c.
a must be a function, because on the righthand side of =, it is a function application.
a takes two arguments: (a b c) and 0. 0 is an integer, so the 2nd parameter of a must be an integer type.
Look inside (a b c), c is the 2nd arguement, so c must be integer.
We can't infer on b. So b is 'a.
Since (a b c) can be the 1st argument of a, and (a b c) itself is an application on function a, the return type of a will have the same type of b which is 'a.
Combine information above together, you get f: ('a -> int -> 'a) -> 'a -> int -> 'a.
If you want to learn it formally, https://realworldocaml.org/ is your friend.

Frequency Trees

I am currently revising for my programming exam (I am new at programming) and I came across with an exercise asking me to implement a functions which "take a frequency tree and list of bits to a value in the Frequency tree and return the remaining bits in a list".
the part that i have trouble understanding is the type that I was given:
FreqTree a -> [bit] -> (a,[bit])
what does the (a, [bit]) actually mean? is a just a value?
Thanks heaps
The type (a, b) is a tuple or pair containing both an a and a b. In Haskell lowercase types are actually type variables, or unknowns. If they are written in a type then it means that the type is invariant to the actual type that variable represents.
If we read the description of this function carefully we can see it is reflective of the type:
take a frequency tree and list of bits to a value in the Frequency tree and return the remaining bits in a list
A function type like
a -> b -> c
can be read as a function from an a and a b to a c. In fact, to further cement the notion of and we had before we can write an equivalent function of type
(a, b) -> c
repeating the idea that tuple types should be read as "and". This conversion is called curry
curry :: (a -> b -> c) -> ((a, b) -> c)
curry f (a, b) = f a b
Using this notion, the description of the function translates to
(FreqTree, ListOfBits) -> (ValueInFreqTree, ListOfRemainingBits)
take
a frequency tree *and* list of bits
to
a value in the Frequency tree *and* the remaining bits in a list
From here we just do a little pattern matching against the type given
(FreqTree , ListOfBits) -> (ValueInFreqTree, ListOfRemainingBits)
FreqTree -> ListOfBits -> (ValueInFreqTree, ListOfRemainingBits)
FreqTree a -> [bit] -> (a , [bit] )
The first step above is the opposite of curry, called uncurry, and the second step compares our expected type to the type given. Here we can see some good parity—list of bits map to [bit] and FreqTree maps to FreqTree a.
So the final bit is to figure out how that type variable a works. This requires understanding the meaning of the parameterized type FreqTree a. Since frequency trees might contain any type of thing caring not about its particular form and only about the ability to compute its frequency, it's nice to parameterize the type by it's value. You might write
FreqTree value
where, again, the lowercase name represents a type variable. In fact, we can do this substitution in the previous type as well, value for a
FreqTree value -> [bit] -> (value, [bit])
and now perhaps this type has taken its most clear form. Given a FreqTree containing unknown types marked as value and a list of bit, we return a particular value and another list of bit.