I have a type class defined like this :
class Repo e ne | ne -> e, e -> ne where
eTable :: Table (Relation e)
And when I try to compile it I get this :
* Couldn't match type `Database.Selda.Generic.Rel
(GHC.Generics.Rep e0)'
with `Database.Selda.Generic.Rel (GHC.Generics.Rep e)'
Expected type: Table (Relation e)
Actual type: Table (Relation e0)
NB: `Database.Selda.Generic.Rel' is a type function, and may not be injective
The type variable `e0' is ambiguous
* In the ambiguity check for `eTable'
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the class method:
eTable :: forall e ne. Repo e ne => Table (Relation e)
In the class declaration for `Repo'
|
41 | eTable :: Table (Relation e)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I was expecting everything to be unambiguous since I've explicitly stated that e determines ne and vice versa.
However, if I try to define my class like this just for the testing purposes, it compiles :
data Test a = Test a
class Repo e ne | ne -> e, e -> ne where
eTable :: Maybe (Test e)
I'm not quite sure what is the deal with Table and Relation types that causes this.
Test is injective, since it is a type constructor.
Relation is not injective, since it is a type family.
Hence the ambiguity.
Silly example:
type instance Relation Bool = ()
type instance Relation String = ()
instance Repo Bool Ne where
eTable :: Table ()
eTable = someEtable1
instance Repo String Ne where
eTable :: Table ()
eTable = someEtable2
Now, what is eTable :: Table () ? It could be the one from the first or the second instance. It is ambiguous since Relation is not injective.
The source of the ambiguity actually has nothing to do with ne not being used in the class (which you headed off by using functional dependencies).
The key part of the error message is:
Expected type: Table (Relation e)
Actual type: Table (Relation e0)
NB: `Database.Selda.Generic.Rel' is a type function, and may not be injective
Note that it's the e that it's having trouble matching up, and the NB message drawing your attention to the issue of type functions and injectivity (you really have to know what that all means for the message to be useful, but it has all the terms you need to look up to understand what's going on, so it's quite good as programming error messages go).
The issue it's complaining about is a key difference between type constructors and type families. Type constructors are always injective, while type functions in general (and type families in particular) do not have to be.
In standard Haskell with no extensions, the only way you can build compound type expressions was using type constructors, such as the left-hand side Test in your data Test a = Test a. I can apply Test (of kind * -> *) to a type like Int (of kind *) to get a type Test Int (of kind *). Type constructors are injective, which means for any two distinct types a and b, Test a is a distinct type from Test b1. This means that when type checking you can "run them backwards"; if I've got two types t1 and t2 that are each the result of applying Test, and I know that t1 and t2 are supposed to be equal, then I can "unapply" Test to get the argument types and check whether those are equal (or infer what one of them is if it was something I hadn't figured out yet and the other is known, or etc).
Type families (or any other form of type function that isn't known to be injective) don't provide us that guarantee. If I have two types t1 and t2 that are supposed to be equal, and they're both the result of applying some TypeFamily, there's no way to go from the resulting types to the types that TypeFamily was applied to. And in particular, there's no way to conclude from the fact that TypeFamily a and TypeFamily b are equal that a and b are equal as well; the type family might just happen to map two distinct types a and b to the same result (the definition of injectivitiy is that it doesn't do that). So if I knew which type a was but didn't know b, knowing that TypeFamily a and TypeFamily b are equal doesn't give me any more information about what type b should be.
Unfortunately, since standard Haskell only has type constructors, Haskell programmers get well-trained to just presume that the type checker can work backwards through compound types to connect up the components. We often don't even notice that the type checker needs to work backwards, we're so used to just looking at type expressions with similar structure and leaping to the obvious conclusions without working through all the steps that the type checker has to go through. But because type checking is based on working out the type of every expression both bottom-up2 and top-down3 and confirming that they are consistent, type checking expressions whose types involve type families can easily run into ambiguity problems where it looks "obviously" unambiguous to us humans.
In your Repo example, consider how the type checker will deal with a position where you use eTable, with (Int, Bool) for e, say. The top-down view will see that it's used in a context where some Table (Relation (Int, Bool)) is required. It'll compute what Relation (Int, Bool) evaluates to: say it's Set Int, so we need Table (Set Int). The bottom-up pass just says eTable can be Table (Relation e) for any e.
All of our experience with standard Haskell tells us that this is obvious, we just instantiate e to (Int, Bool), Relation (Int, Bool) evaluates to Set Int again and we're done. But that's not actually how it works. Because Relation isn't injective there could be some other choice for e for which gives us Set Int for Relation e: perhaps Int. But if we choose e to be (Int, Bool) or Int we need to look for two different Repo instances, which will have different implementations for eTable, even though their type is the same.
Even adding a type annotation every time you use eTable like eTable :: Table (Relation (Int, Bool)) doesn't help. The type annotation only adds extra information to the top-down view, which we often already have anyway. The type-checker is still stuck with the problem that there could be (whether or not there actually are) other choices of e than (Int, Bool) which lead to eTable matching that type annotation, so it doesn't know which instance to use. Any possible use of eTable will have this problem, so it gets reported as an error when you're defining the class. It's basically for the same reason you get problems when you have a class with some members whose types don't use all of the type variables in the class head; you have to consider "variable only used under a type family" as much the same as "variable isn't used at all".
You could address this by adding a Proxy argument to eTable so that there's something fixing the type variable e that the type checker can "run backwards". So eTable :: Proxy e -> Table (Relation e).
Alternatively, with the TypeApplications extension you now can do as the error message suggests and turn on AllowAmbiguousTypes to get the class accepted, and then use things like eTable #(Int, Bool) to tell the compiler which choice for e you want. The reason this works where the type annotation eTable :: Table (Relation (Int, Bool)) doesn't work is the type annotation is extra information added to the context when the type checker is looking top-down, but the type application adds extra information when the type checker is looking bottom-up. Instead of "this expression is required to have a type that unifies with this type" it's "this polymorphic function is instantiated at this type".
1 Type constructors are actually even more restricted than just injectivity; applying Test to any type a results in a type with known structure Test a, so the entire universe of Haskell types is straightforwardly mirrored in types of the form Test t. A more general injective type function could instead do more "rearranging", such as mapping Int to Bool so long as it didn't also map Bool to Bool.
2 From the type produced by combining the sub-parts of the expression
3 From the type required of the context in which it is used
Related
Consider the following simple snippet of PureScript code
a :: Int
a = 5
b :: Int
b = 7
c = a + b
main ∷ Effect Unit
main = do
logShow c
The program successfully infers the type of C to be Int, and outputs the expected result:
12
However, it also produces this warning:
No type declaration was provided for the top-level declaration of c.
It is good practice to provide type declarations as a form of documentation.
The inferred type of c was:
Int
in value declaration c
I find this confusing, since I would expect the Int type for C to be safely inferred. Like it often says in the docs, "why derive types when the compiler can do it for you?" This seems like a textbook example of the simplest and most basic type inference.
Is this warning expected? Is there a standard configuration that would suppress it?
Does this warning indicate that every variable should in fact be explicitly typed?
In most cases, and certainly in the simplest cases, the types can be inferred unambiguously, and indeed, in those cases type signatures are not necessary at all. This is why simpler languages, such as F#, Ocaml, or Elm, do not require type signatures.
But PureScript (and Haskell) has much more complicated cases too. Constrained types are one. Higher-rank types are another. It's a whole mess. Don't get me wrong, I love me some high-power type system, but the sad truth is, type inference works ambiguously with all of that stuff a lot of the time, and sometimes doesn't work at all.
In practice, even when type inference does work, it turns out that its results may be wildly different from what the developer intuitively expects, leading to very hard to debug issues. I mean, type errors in PureScript can be super vexing as it is, but imagine that happening across multiple top-level definitions, across multiple modules, even perhaps across multiple libraries. A nightmare!
So over the years a consensus has formed that overall it's better to have all the top-level definitions explicitly typed, even when it's super obvious. It makes the program much more understandable and puts constraints on the typechecker, providing it with "anchor points" of sorts, so it doesn't go wild.
But since it's not a hard requirement (most of the time), it's just a warning, not an error. You can ignore it if you wish, but do that at your own peril.
Now, another part of your question is whether every variable should be explicitly typed, - and the answer is "no".
As a rule, every top-level binding should be explicitly typed (and that's where you get a warning), but local bindings (i.e. let and where) don't have to, unless you need to clarify something that the compiler can't infer.
Moreover, in PureScript (and modern Haskell), local bindings are actually "monomorphised" - that's a fancy term basically meaning they can't be generic unless explicitly specified. This solves the problem of all the ambiguous type inference, while still working intuitively most of the time.
You can notice the difference with the following example:
f :: forall a b. Show a => Show b => a -> b -> String
f a b = s a <> s b
where
s x = show x
On the second line s a <> s b you get an error saying "Could not match type b with type a"
This happens because the where-bound function s has been monomorphised, - meaning it's not generic, - and its type has been inferred to be a -> String based on the s a usage. And this means that s b usage is ill-typed.
This can be fixed by giving s an explicit type signature:
f :: forall a b. Show a => Show b => a -> b -> String
f a b = s a <> s b
where
s :: forall x. Show x => x -> String
s x = show x
Now it's explicitly specified as generic, so it can be used with both a and b parameters.
I am approaching the Haskell programming language, and I have a background of Scala and Java developer.
I was reading the theory behind type constructors, but I cannot understand if they can be considered types. I mean, in Scala, you use the keywords class or trait to define type constructors. Think about List[T], or Option[T]. Also in Haskell, you use the same keyword data, that is used for defining new types.
So, are type constructors also types?
Let's look at an analogy: functions. In some branches of mathematics, functions are called value constructors, because that's what they do: you put one or more values in, and they construct a new value out of those.
Type constructors are exactly the same thing, except on the type level: you put one or more types in, and they construct a new type out of those. They are, in some sense, functions on the type level.
Now, to our analogy: what is the analog of the question you are asking? Well, it is this: "Can value constructors (i.e. functions) be considered as values in functional programming languages?"
And the answer is: it depends on the programming language. Now, for functional programming languages, the answer is "Yes" for almost all (if not all) of them. It depends on your definition of what a "functional programming language" is. Some people define a functional programming language as a programming language which has functions as values, so the answer will be trivially "Yes" by definition. But, some people define a functional programming language as a programming language which does not allow side-effects, and in such a language, it is not necessarily true that functions are values.
The most famous example may be John Backus' FP, from his seminal paper Can Programming Be Liberated from the von Neumann Style? – a functional style and its algebra of programs. In FP, there is a hierarchy of "function-like" things. Functions can only deal with values, and functions themselves are not values. However, there is a concept of "functionals" which are "function constructors", i.e. they can take functions (and also values) as input and/or produce functions as output, but they cannot take functionals as input and/or produce them as output.
So, FP is arguably a functional programming language, but it does not have functions as values.
Note: functions as values is also called "first-class functions" and functions that take functions as input or return them as output are called "higher-order functions".
If we look at some types:
1 :: Int
[1] :: List Int
add :: Int → Int
map :: (a → b, List a) → b
You can see that we can easily say: any value whose type has an arrow in it, is a function. Any value whose type has more than one arrow in it, is a higher-order function.
Again, the same applies to type constructors, since they are really the same thing except on the type level. In some languages, type constructors can be types, in some they can't. For example, in Java and C♯, type constructors are not types. You cannot have a List<List> in C♯, for example. You can write down the type List<List> in Java, but that is misleading, since the two Lists mean different things: the first List is the type constructor, the second List is the raw type, so this is in fact not using a type constructor as a type.
What is the equivalent to our types example above?
Int :: Type
List :: Type ⇒ Type
→ :: (Type, Type) ⇒ Type
Functor :: (Type ⇒ Type) ⇒ Type
(Note, how we always have Type? Indeed, we are only dealing with types, so we normally don't write Type but instead simply write *, pronounced "Type"):
Int :: *
List :: * ⇒ *
→ :: (*, *) ⇒ *
Functor :: (* ⇒ *) ⇒ *
So, Int is a proper type, List is a type constructor that takes one type and produces a type, → (the function type constructor) takes two types and returns a type (assuming only unary functions, e.g. using currying or passing tuples), and Functor is a type constructor, which itself takes a type constructor and returns a type.
Theses "type-types" are called kinds. Like with functions, anything with an arrow is a type constructor, and anything with more than one arrow is a higher-kinded type constructor.
And like with functions, some languages allow higher-kinded type constructors and some don't. The two languages you mention in your question, Scala and Haskell do, but as mentioned above, Java and C♯ don't.
However, there is a complication when we look at your question:
So, are type constructors also types?
Not really, no. At least not in any language I know about. See, while you can have higher-kinded type constructors that take type constructors as input and/or return them as output, you cannot have an expression or a value or a variable or a parameter which has a type constructor as its type. You cannot have a function that takes a List or returns a List. You cannot have a variable of type Monad. But, you can have a variable of type Int.
So, clearly, there is a difference between types and type constructors.
Well, types and type constructors have a calculus of their own and they each have kinds. If you use :k (Maybe Int) in ghci for example, you'll get *, now this is a proper type and it (usually) has inhabitants. In this case Nothing, Just 42, etc. * now has a more descriptive alias Type.
Now you can look at the kind of the constructor that is Maybe, and :k Maybe will give you * -> *. With the alias, this is Type -> Type which is what you expect. It takes a Type and constructs a Type. Now if you see types as set of values, one good question is what set of values do Maybe has? Well, none because it is not really a type. You might attempt something like Just but that has type a -> Maybe a with kind Type, rather than Maybe with kind Type -> Type.
At least in Haskell, there is a hierarchy that can roughly be described as follows.
Terms are things that exist at run-time, values like 1, 'a', and (+), for example.
Each term has a type, like Int or Char or Int -> Int -> Int.
Each type has a kind, and all types have the same kind, namely *.
A type constructor like [], though, has kind * -> *, so it is not a type. Instead, it is a mapping from a type to a type.
There are other kinds as well, including (in addition to * and * -> *, with an example of each):
* -> * -> * (Either)
(* -> *) -> * -> * (ReaderT, a monad transformer)
Constraint (Num Int)
* -> Constraint (Num; this is the kind of a type class)
I am trying to understand how to think about type classes in Haskell versus traits in Scala.
My understanding is that type classes are primarily important at compile time in Haskell and not at runtime anymore, on the other hand traits in Scala are important both at compile time and run time. I want to illustrate this idea with a simple example, and I want to know if this viewpoint of mine is correct or not.
First, let us consider type classes in Haskell:
Let's take a simple example. The type class Eq.
For example, Int and Char are both instances of Eq. So it is possible to create a polymorphic List that is also an instance of Eq and can either contain Ints or Chars but not both in the same List.
My question is : is this the only reason why type classes exist in Haskell?
The same question in other words:
Type classes enable to create polymorphic types ( in this example a polymorphic List) that support operations that are defined in a given type class ( in this example the operation == defined in the type class Eq) but that is their only reason for existence, according to my understanding. Is this understanding of mine correct?
Is there any other reason why type classes exist in ( standard ) Haskell?
Is there any other use case in which type classes are useful in standard Haskell ? I cannot seem to find any.
Since Haskell's Lists are homogeneous, it is not possible to put Char and Int into the same list. So the usefulness of type classes, according to my understanding, is exhausted at compile time. Is this understanding of mine correct?
Now, let's consider the analogous List example in Scala:
Lets define a trait Eq with an equals method on it.
Now let's make Char and Int implement the trait Eq.
Now it is possible to create a List[Eq] in Scala that accepts both Chars and Ints into the same List ( Note that this - putting different type of elements into the same List - is not possible Haskell, at least not in standard Haskell 98 without extensions)!
In the case of the Haskell's List, the existence of type classes is important/useful only for type checking at compile time, according to my understanding.
In contrast, the existence of traits in Scala is important both at compile time for type checking and at run type for polymorphic dispatch on the actual runtime type of the object in the List when comparing two Lists for equality.
So, based on this simple example, I came to the conclusion that in Haskell type classes are primarily important/used at compilation time, in contrast, Scala's traits are important/used both at compile time and run time.
Is this conclusion of mine correct?
If not, why not ?
EDIT:
Scala code in response to n.m.'s comments:
case class MyInt(i:Int) {
override def equals(b:Any)= i == b.asInstanceOf[MyInt].i
}
case class MyChar(c:Char) {
override def equals(a:Any)= c==a.asInstanceOf[MyChar].c
}
object Test {
def main(args: Array[String]) {
val l1 = List(MyInt(1), MyInt(2), MyChar('a'), MyChar('b'))
val l2 = List(MyInt(1), MyInt(2), MyChar('a'), MyChar('b'))
val l3 = List(MyInt(1), MyInt(2), MyChar('a'), MyChar('c'))
println(l1==l1)
println(l1==l3)
}
}
This prints:
true
false
I will comment on the Haskell side.
Type classes bring restricted polymorphism in Haskell, wherein a type variable a can still be quantified universally, but ranges over only a subset of all the types -- namely, the types for which an instance of the type class is available.
Why restricted polymorphism is useful? A nice example would be the equality operator
(==) :: ?????
What its type should be? Intuitively, it takes two values of the same type and returns a boolean, so:
(==) :: a -> a -> Bool -- (1)
But the typing above is not entirely honest, since it allows one to apply == to any type a, including function types!
(\x :: Integer -> x + x) == (\x :: Integer -> 2*x)
The above would pass type checking if (1) were the typing for (==), since both arguments are of the same type a = (Integer -> Integer). However, we can not effectively compare two functions: well-known Computability results tell us that there is no algorithm to do that in general.
So, what we could do to implement (==)?
Option 1: at run time, if a function (or any other value involving functions -- such as a list of functions) is found to be passed to (==), raise an exception. This is what e.g. ML does. Typed programs can now "go wrong", despite checking types at compile time.
Option 2: introduce a new kind of polymorphism, restricting a to the function-free types. For instance, ww could have (==) :: forall-non-fun a. a -> a -> Bool so that comparing functions yields to a type error. Haskell exploits type classes to obtain exactly that.
So, Haskell type classes allow one to type (==) "honestly", ensuring no error at run time, and without being overly restrictive. Of course, the power of type classes goes far beyond of that but, at least in my own view, they primary purpose is to allow restricted polymorphism, in a very general and flexible way. Indeed, with type classes the programmer can define their own restrictions on the universal type quantifications.
I've been doing dev in F# for a while and I like it. However one buzzword I know doesn't exist in F# is higher-kinded types. I've read material on higher-kinded types, and I think I understand their definition. I'm just not sure why they're useful. Can someone provide some examples of what higher-kinded types make easy in Scala or Haskell, that require workarounds in F#? Also for these examples, what would the workarounds be without higher-kinded types (or vice-versa in F#)? Maybe I'm just so used to working around it that I don't notice the absence of that feature.
(I think) I get that instead of myList |> List.map f or myList |> Seq.map f |> Seq.toList higher kinded types allow you to simply write myList |> map f and it'll return a List. That's great (assuming it's correct), but seems kind of petty? (And couldn't it be done simply by allowing function overloading?) I usually convert to Seq anyway and then I can convert to whatever I want afterwards. Again, maybe I'm just too used to working around it. But is there any example where higher-kinded types really saves you either in keystrokes or in type safety?
So the kind of a type is its simple type. For instance Int has kind * which means it's a base type and can be instantiated by values. By some loose definition of higher-kinded type (and I'm not sure where F# draws the line, so let's just include it) polymorphic containers are a great example of a higher-kinded type.
data List a = Cons a (List a) | Nil
The type constructor List has kind * -> * which means that it must be passed a concrete type in order to result in a concrete type: List Int can have inhabitants like [1,2,3] but List itself cannot.
I'm going to assume that the benefits of polymorphic containers are obvious, but more useful kind * -> * types exist than just the containers. For instance, the relations
data Rel a = Rel (a -> a -> Bool)
or parsers
data Parser a = Parser (String -> [(a, String)])
both also have kind * -> *.
We can take this further in Haskell, however, by having types with even higher-order kinds. For instance we could look for a type with kind (* -> *) -> *. A simple example of this might be Shape which tries to fill a container of kind * -> *.
data Shape f = Shape (f ())
Shape [(), (), ()] :: Shape []
This is useful for characterizing Traversables in Haskell, for instance, as they can always be divided into their shape and contents.
split :: Traversable t => t a -> (Shape t, [a])
As another example, let's consider a tree that's parameterized on the kind of branch it has. For instance, a normal tree might be
data Tree a = Branch (Tree a) a (Tree a) | Leaf
But we can see that the branch type contains a Pair of Tree as and so we can extract that piece out of the type parametrically
data TreeG f a = Branch a (f (TreeG f a)) | Leaf
data Pair a = Pair a a
type Tree a = TreeG Pair a
This TreeG type constructor has kind (* -> *) -> * -> *. We can use it to make interesting other variations like a RoseTree
type RoseTree a = TreeG [] a
rose :: RoseTree Int
rose = Branch 3 [Branch 2 [Leaf, Leaf], Leaf, Branch 4 [Branch 4 []]]
Or pathological ones like a MaybeTree
data Empty a = Empty
type MaybeTree a = TreeG Empty a
nothing :: MaybeTree a
nothing = Leaf
just :: a -> MaybeTree a
just a = Branch a Empty
Or a TreeTree
type TreeTree a = TreeG Tree a
treetree :: TreeTree Int
treetree = Branch 3 (Branch Leaf (Pair Leaf Leaf))
Another place this shows up is in "algebras of functors". If we drop a few layers of abstractness this might be better considered as a fold, such as sum :: [Int] -> Int. Algebras are parameterized over the functor and the carrier. The functor has kind * -> * and the carrier kind * so altogether
data Alg f a = Alg (f a -> a)
has kind (* -> *) -> * -> *. Alg useful because of its relation to datatypes and recursion schemes built atop them.
-- | The "single-layer of an expression" functor has kind `(* -> *)`
data ExpF x = Lit Int
| Add x x
| Sub x x
| Mult x x
-- | The fixed point of a functor has kind `(* -> *) -> *`
data Fix f = Fix (f (Fix f))
type Exp = Fix ExpF
exp :: Exp
exp = Fix (Add (Fix (Lit 3)) (Fix (Lit 4))) -- 3 + 4
fold :: Functor f => Alg f a -> Fix f -> a
fold (Alg phi) (Fix f) = phi (fmap (fold (Alg phi)) f)
Finally, though they're theoretically possible, I've never see an even higher-kinded type constructor. We sometimes see functions of that type such as mask :: ((forall a. IO a -> IO a) -> IO b) -> IO b, but I think you'll have to dig into type prolog or dependently typed literature to see that level of complexity in types.
Consider the Functor type class in Haskell, where f is a higher-kinded type variable:
class Functor f where
fmap :: (a -> b) -> f a -> f b
What this type signature says is that fmap changes the type parameter of an f from a to b, but leaves f as it was. So if you use fmap over a list you get a list, if you use it over a parser you get a parser, and so on. And these are static, compile-time guarantees.
I don't know F#, but let's consider what happens if we try to express the Functor abstraction in a language like Java or C#, with inheritance and generics, but no higher-kinded generics. First try:
interface Functor<A> {
Functor<B> map(Function<A, B> f);
}
The problem with this first try is that an implementation of the interface is allowed to return any class that implements Functor. Somebody could write a FunnyList<A> implements Functor<A> whose map method returns a different kind of collection, or even something else that's not a collection at all but is still a Functor. Also, when you use the map method you can't invoke any subtype-specific methods on the result unless you downcast it to the type that you're actually expecting. So we have two problems:
The type system doesn't allow us to express the invariant that the map method always returns the same Functor subclass as the receiver.
Therefore, there's no statically type-safe manner to invoke a non-Functor method on the result of map.
There are other, more complicated ways you can try, but none of them really works. For example, you could try augment the first try by defining subtypes of Functor that restrict the result type:
interface Collection<A> extends Functor<A> {
Collection<B> map(Function<A, B> f);
}
interface List<A> extends Collection<A> {
List<B> map(Function<A, B> f);
}
interface Set<A> extends Collection<A> {
Set<B> map(Function<A, B> f);
}
interface Parser<A> extends Functor<A> {
Parser<B> map(Function<A, B> f);
}
// …
This helps to forbid implementers of those narrower interfaces from returning the wrong type of Functor from the map method, but since there is no limit to how many Functor implementations you can have, there is no limit to how many narrower interfaces you'll need.
(EDIT: And note that this only works because Functor<B> appears as the result type, and so the child interfaces can narrow it. So AFAIK we can't narrow both uses of Monad<B> in the following interface:
interface Monad<A> {
<B> Monad<B> flatMap(Function<? super A, ? extends Monad<? extends B>> f);
}
In Haskell, with higher-rank type variables, this is (>>=) :: Monad m => m a -> (a -> m b) -> m b.)
Yet another try is to use recursive generics to try and have the interface restrict the result type of the subtype to the subtype itself. Toy example:
/**
* A semigroup is a type with a binary associative operation. Law:
*
* > x.append(y).append(z) = x.append(y.append(z))
*/
interface Semigroup<T extends Semigroup<T>> {
T append(T arg);
}
class Foo implements Semigroup<Foo> {
// Since this implements Semigroup<Foo>, now this method must accept
// a Foo argument and return a Foo result.
Foo append(Foo arg);
}
class Bar implements Semigroup<Bar> {
// Any of these is a compilation error:
Semigroup<Bar> append(Semigroup<Bar> arg);
Semigroup<Foo> append(Bar arg);
Semigroup append(Bar arg);
Foo append(Bar arg);
}
But this sort of technique (which is rather arcane to your run-of-the-mill OOP developer, heck to your run-of-the-mill functional developer as well) still can't express the desired Functor constraint either:
interface Functor<FA extends Functor<FA, A>, A> {
<FB extends Functor<FB, B>, B> FB map(Function<A, B> f);
}
The problem here is this doesn't restrict FB to have the same F as FA—so that when you declare a type List<A> implements Functor<List<A>, A>, the map method can still return a NotAList<B> implements Functor<NotAList<B>, B>.
Final try, in Java, using raw types (unparametrized containers):
interface FunctorStrategy<F> {
F map(Function f, F arg);
}
Here F will get instantiated to unparametrized types like just List or Map. This guarantees that a FunctorStrategy<List> can only return a List—but you've abandoned the use of type variables to track the element types of the lists.
The heart of the problem here is that languages like Java and C# don't allow type parameters to have parameters. In Java, if T is a type variable, you can write T and List<T>, but not T<String>. Higher-kinded types remove this restriction, so that you could have something like this (not fully thought out):
interface Functor<F, A> {
<B> F<B> map(Function<A, B> f);
}
class List<A> implements Functor<List, A> {
// Since F := List, F<B> := List<B>
<B> List<B> map(Function<A, B> f) {
// ...
}
}
And addressing this bit in particular:
(I think) I get that instead of myList |> List.map f or myList |> Seq.map f |> Seq.toList higher kinded types allow you to simply write myList |> map f and it'll return a List. That's great (assuming it's correct), but seems kind of petty? (And couldn't it be done simply by allowing function overloading?) I usually convert to Seq anyway and then I can convert to whatever I want afterwards.
There are many languages that generalize the idea of the map function this way, by modeling it as if, at heart, mapping is about sequences. This remark of yours is in that spirit: if you have a type that supports conversion to and from Seq, you get the map operation "for free" by reusing Seq.map.
In Haskell, however, the Functor class is more general than that; it isn't tied to the notion of sequences. You can implement fmap for types that have no good mapping to sequences, like IO actions, parser combinators, functions, etc.:
instance Functor IO where
fmap f action =
do x <- action
return (f x)
-- This declaration is just to make things easier to read for non-Haskellers
newtype Function a b = Function (a -> b)
instance Functor (Function a) where
fmap f (Function g) = Function (f . g) -- `.` is function composition
The concept of "mapping" really isn't tied to sequences. It's best to understand the functor laws:
(1) fmap id xs == xs
(2) fmap f (fmap g xs) = fmap (f . g) xs
Very informally:
The first law says that mapping with an identity/noop function is the same as doing nothing.
The second law says that any result that you can produce by mapping twice, you can also produce by mapping once.
This is why you want fmap to preserve the type—because as soon as you get map operations that produce a different result type, it becomes much, much harder to make guarantees like this.
I don't want to repeat information in some excellent answers already here, but there's a key point I'd like to add.
You usually don't need higher-kinded types to implement any one particular monad, or functor (or applicative functor, or arrow, or ...). But doing so is mostly missing the point.
In general I've found that when people don't see the usefulness of functors/monads/whatevers, it's often because they're thinking of these things one at a time. Functor/monad/etc operations really add nothing to any one instance (instead of calling bind, fmap, etc I could just call whatever operations I used to implement bind, fmap, etc). What you really want these abstractions for is so you can have code that works generically with any functor/monad/etc.
In a context where such generic code is widely used, this means any time you write a new monad instance your type immediately gains access to a large number of useful operations that have already been written for you. That's the point of seeing monads (and functors, and ...) everywhere; not so that I can use bind rather than concat and map to implement myFunkyListOperation (which gains me nothing in itself), but rather so that when I come to need myFunkyParserOperation and myFunkyIOOperation I can re-use the code I originally saw in terms of lists because it's actually monad-generic.
But to abstract across a parameterised type like a monad with type safety, you need higher-kinded types (as well explained in other answers here).
For a more .NET-specific perspective, I wrote a blog post about this a while back. The crux of it is, with higher-kinded types, you could potentially reuse the same LINQ blocks between IEnumerables and IObservables, but without higher-kinded types this is impossible.
The closest you could get (I figured out after posting the blog) is to make your own IEnumerable<T> and IObservable<T> and extended them both from an IMonad<T>. This would allow you to reuse your LINQ blocks if they're denoted IMonad<T>, but then it's no longer typesafe because it allows you to mix-and-match IObservables and IEnumerables within the same block, which while it may sound intriguing to enable this, you'd basically just get some undefined behavior.
I wrote a later post on how Haskell makes this easy. (A no-op, really--restricting a block to a certain kind of monad requires code; enabling reuse is the default).
The most-used example of higher-kinded type polymorphism in Haskell is the Monad interface. Functor and Applicative are higher-kinded in the same way, so I'll show Functor in order to show something concise.
class Functor f where
fmap :: (a -> b) -> f a -> f b
Now, examine that definition, looking at how the type variable f is used. You'll see that f can't mean a type that has value. You can identify values in that type signature because they're arguments to and results of a functions. So the type variables a and b are types that can have values. So are the type expressions f a and f b. But not f itself. f is an example of a higher-kinded type variable. Given that * is the kind of types that can have values, f must have the kind * -> *. That is, it takes a type that can have values, because we know from previous examination that a and b must have values. And we also know that f a and f b must have values, so it returns a type that must have values.
This makes the f used in the definition of Functor a higher-kinded type variable.
The Applicative and Monad interfaces add more, but they're compatible. This means that they work on type variables with kind * -> * as well.
Working on higher-kinded types introduces an additional level of abstraction - you aren't restricted to just creating abstractions over basic types. You can also create abstractions over types that modify other types.
Why might you care about Applicative? Because of traversals.
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
type Traversal s t a b = forall f. Applicative f => (a -> f b) -> s -> f t
Once you've written a Traversable instance, or a Traversal for some type, you can use it for an arbitrary Applicative.
Why might you care about Monad? One reason is streaming systems like pipes, conduit, and streaming. These are entirely non-trivial systems for working with effectful streams. With the Monad class, we can reuse all that machinery for whatever we like, rather than having to rewrite it from scratch each time.
Why else might you care about Monad? Monad transformers. We can layer monad transformers however we like to express different ideas. The uniformity of Monad is what makes this all work.
What are some other interesting higher-kinded types? Let's say ... Coyoneda. Want to make repeated mapping fast? Use
data Coyoneda f a = forall x. Coyoneda (x -> a) (f x)
This works or any functor f passed to it. No higher-kinded types? You'll need a custom version of this for each functor. This is a pretty simple example, but there are much trickier ones you might not want to have to rewrite every time.
Recently stated learning a bit about higher kinded types. Although it's an interesting idea, to be able to have a generic that needs another generic but apart from library developers, i do not see any practical use in any real app. I use scala in business app, i have also seen and studied the code of some nicely designed sgstems and libraries like kafka, akka and some financial apps. Nowhere I found any higher kinded type in use.
It seems like they are nice for academia or similar but the market doesn't need it or hasn't reached to a point where HKT has any practical uses or proves to be better than other existing techniques. To me it's something that you can use to impress others or write blog posts but nothing more than that. It's like multiverse or string theory. Looks nice on paper, gives you hours to speak about but nothing real
(sorry if you don't have any interest in theoratical physiss). One prove is that all the answers above, they all brilliantly describe the mechanics fail to cite one true real case where we would need it despite being the fact it's been 6+ years since OP posted it.
I'm having a hard time understanding Higher Kind vs Higher Rank types. Kind is pretty simple (thanks Haskell literature for that) and I used to think rank is like kind when talking about types but apparently not! I read the Wikipedia article to no avail. So can someone please explain what is a Rank? and what is meant by Higher Rank? Higher Rank Polymorphism? how that comes to Kinds (if any) ? Comparing Scala and Haskell would be awesome too.
The concept of rank is not really related to the concept of kinds.
The rank of a polymorphic type system describes where foralls may appear in types. In a rank-1 type system foralls may only appear at the outermost level, in a rank-2 type system they may appear at one level of nesting and so on.
So for example forall a. Show a => (a -> String) -> a -> String would be a rank-1 type and forall a. Show a => (forall b. Show b => b -> String) -> a -> String would be a rank-2 type. The difference between those two types is that in the first case, the first argument to the function can be any function that takes one showable argument and returns a String. So a function of type Int -> String would be a valid first argument (like a hypothetical function intToString), so would a function of type forall a. Show a => a -> String (like show). In the second case only a function of type forall a. Show a => a -> String would be a valid argument, i.e. show would be okay, but intToString wouldn't be. As a consequence the following function would be a legal function of the second type, but not the first (where ++ is supposed to represent string concatenation):
higherRankedFunction(f, x) = f("hello") ++ f(x) ++ f(42)
Note that here the function f is applied to (potentially) three different types of arguments. So if f were the function intToString this would not work.
Both Haskell and Scala are Rank-1 (so the above function can not be written in those languages) by default. But GHC contains a language extension to enable Rank-2 polymorphism and another one to enable Rank-n polymorphism for arbitrary n.