Why does the type of local variables that are values affect the type of input variables in the type signature of a function? - haskell

Prelude> func f = [(show s, f == s) | s <- [0, 1..10]]
Prelude> :type func
func :: (Num a, Enum a, Show a, Eq a) => a -> [(String, Bool)]
I would expect f to just be an instance of Eq a but all the class constraints applied to s are also applied to f for some reason. Replacing s with any constant removes the relevant type constraint for f, and replacing s in the equality removes all class constraints except Eq a for f.
Can someone explain to me why does the type of local variables that are values affect the type of input variables that are values?

Eq doesn't exist in a vacuum. To compare two things for equality, you have to have two things. And, crucially, those two things have to be of the same type. In Haskell, 0 == "A" isn't just false; it's a type error. It literally doesn't make sense.
f == s
When the compiler sees this, even if it knows nothing else about the types of f and s, it knows what (==) is. (==) is a function with the following signature.
(==) :: Eq a => a -> a -> Bool
Both arguments are of the same type. So now and forevermore, for the rest of type-checking this expression, we must have f and s of the same type. Anything required of s is also required of f. And s takes values from [0, 1..10]. Your type constraints come as follows
Num is required since s takes values from a list of literal integers.
Enum is required by the [..] list enumeration syntax.
Show is required by show s.
Eq is required by the f == s equality expression.
Now, if we replace s with a constant, we get something like
func f = [(show s, f == 0) | s <- [0, 1..10]]
Now f is being compared with 0. It has no relation to s. f requires Eq (for (==)) and Num (since we're comparing against zero, a number). s, on the other hand, requires Enum, Num, Eq, and Show. In the abstract, this should actually be a type error, since we've given no indication as to which type s should be and there aren't enough clues to figure it out. But type defaulting kicks in and we'll get Integer out of it.

Related

Assigning constrained literal to a polymorphic variable

While learning haskell with Haskell Programming from first principles found an exercise that puzzles me.
Here is the short version:
For the following definition:
a) i :: Num a => a
i = 1
b) Try replacing the type signature with the following:
i :: a
The replacement gives me an error:
error:
• No instance for (Num a) arising from the literal ‘1’
Possible fix:
add (Num a) to the context of
the type signature for:
i' :: forall a. a
• In the expression: 1
In an equation for ‘i'’: i' = 1
|
38 | i' = 1
| ^
It is more or less clear for me how Num constraint arises.
What is not clear why assigning 1 to polymorphic variable i' gives the error.
Why this works:
id 1
while this one doesn't:
i' :: a
i' = 1
id i'
Should it be possible to assign a more specific value to a less specific and lose some type info if there are no issues?
This is a common misunderstanding. You probably have something in mind like, in a class-OO language,
class Object {};
class Num: Object { public: Num add(...){...} };
class Int: Num { int i; ... };
And then you would be able to use an Int value as the argument to a function that expects a Num argument, or a Num value as the argument to a function that expects an Object.
But that's not at all how Haskell's type classes work. Num is not a class of values (like, in the above example it would be the class of all values that belong to one of the subclasses). Instead, it's the class of all types that represent specific flavours of numbers.
How is that different? Well, a polymorphic literal like 1 :: Num a => a does not generate a specific Num value that can then be upcasted to a more general class. Instead, it expects the caller to first pick a concrete type in which you want to render the number, then generates the number immediately in that type, and afterwards the type never changes.
In other words, a polymorphic value has an implicit type-level argument. Whoever wants to use i needs to do so in a context where both
It is unambiguous what type a should be used. (It doesn't necessarily need to be fixed right there: the caller could also itself be a polymorphic function.)
The compiler can prove that this type a has a Num instance.
In C++, the analogue of Haskell typeclasses / polymorphic literal is not [sub]classes and their objects, but instead templates that are constrained to a concept:
#include <concepts>
template<typename A>
concept Num = std::constructible_from<A, int>; // simplified
template<Num A>
A poly_1() {
return 1;
}
Now, poly_1 can be used in any setting that demands a type which fulfills the Num concept, i.e. in particular a type that is constructible_from an int, but not in a context which requires some other type.
(In older C++ such a template would just be duck-typed, i.e. it's not explicit that it requires a Num setting but the compiler would just try to use it as such and then give a type error upon noticing that 1 can't be converted to the specified type.)
tl;dr
A value i' declared as i' :: a must be usable¹ in place of any other value, with no exception. 1 is no such a value, as it can't be used, say, where a String is expected, just to make one example.
Longer version
Let's start form a less uncontroversial scenario where you do need a type constraint:
plus :: a -> a -> a
plus x y = x + y
This does not compile, because the signature is equivalent to plus :: forall a. a -> a -> a, and it is plainly not true that the RHS, x + y, is meaningful for any common type a that x and y are inhabitants of. So you can fix the above by providing a constraint guaranteeing that + is possible between two as, and you can do so by putting Num a => right after :: (or even by giving up on polymorphic types and just change a to Int).
But there are functions that don't require any constraints on their arguments. Here's three of them:
id :: a -> a
id x = x
const :: a -> b -> a
const x _ = x
Data.Tuple.swap :: (a, b) -> (b, a)
Data.Tuple.swap (a, b) = (b, a)
You can pass anything to these functions, and they'll always work, because their definitions make no assumption whatsoever on what can be done with those objects, as they just shuffle/ditch them.
Similarly,
i' :: a
i' = 1
cannot compile because it's not true that 1 can represent a value of any type a. It can't represent a String, for instance, whereas the signature i' :: a is expressing the idea that you can put i' in any place, e.g. where a Int is expected, as much as where a generic Num is expected, or where a String is expected, and so on.
In other words, the above signature says that you can use i' in both of these statements:
j = i' + 1
k = i' ++ "str"
So the question is: just like we found some functions that have signatures not constraining their arguments in any way, do we have a value that inhabits every single type you can think of?
Yes, there are some values like that, and here are two of them:
i' :: a
i' = error ""
j' :: a
j' = undefined
They're all "bottoms", or ⊥.
(¹) By "usable" I mean that when you write it in some place where the code compiles, the code keeps compiling.

Why is `succ i` valid where `i :: Num a => a` (and not an `Enum a`)?

This seems to apply to both GHCi and GHC. I'll show an example with GHCi first.
Given i type has been inferred as follows:
Prelude> i = 1
Prelude> :t i
i :: Num p => p
Given that succ is a function defined on Enum:
Prelude> :i Enum
class Enum a where
succ :: a -> a
pred :: a -> a
-- …OMITTED…
and that Num is not a 'subclass' (if I can use that term) of Enum:
class Num a where
(+) :: a -> a -> a
(-) :: a -> a -> a
-- …OMITTED…
why succ i does not return an error?
Prelude> succ i
2 -- works, no error
I would expect :type i to be inferred to something like:
Prelude> i = 1
Prelude> :type i
i :: (Enum p, Num p) => p
(I'm using 'GHC v. 8.6.3')
ADDITION:
After reading #RobinZigmond comment and #AlexeyRomanov answer I have noticed that 1 could be interpreted as one of many types and one of many classes.
Thanks to #AlexeyRomanov answer I understand much more about the defaulting-rules used to decide what type to use for ambiguous expressions.
However I don't feel that Alexey answer addresses exactly my question. My question is about the type of i. It's not about the type of succ i.
It's about the mismatch between succ argument type (an Enum a) and the apparent type of i (a Num a).
I'm now starting to realise that my question must stem from a wrong assumption: 'that once i is inferred to be i :: Num a => a, then i can be nothing else'. Hence I was puzzled to see succ i was evaluated without errors.
GHC also seems to be inferring Enum a in addition to what was explicitly declared.
x :: Num a => a
x = 1
y = succ x -- works
However it is not adding Enum a when the type variable appears as a function:
my_succ :: Num a => a -> a
my_succ z = succ z -- fails compilation
To me it seems that the type constraints attached to a function are stricter to the ones applied to a variable.
GHC is saying my_succ :: forall a. Num a => a -> a and given
forall a doesn't appear in the type-signature of neither i nor x I thought that meant GHC is not going to infer any more classes for my_succ types.
But this seems again wrong: I've checked this idea with the following (first time I type RankNTypes) and apparently GHC still infers Enum a:
{-# LANGUAGE RankNTypes #-}
x :: forall a. Num a => a
x = 1
y = succ x
So it seems that inference rules for functions are stricter than the ones for variables?
Yes, succ i's type is inferred as you expect:
Prelude> :t succ i
succ i :: (Enum a, Num a) => a
This type is ambiguous, but it satisfies the conditions in the defaulting rules for GHCi:
Find all the unsolved constraints. Then:
Find those that are of form (C a) where a is a type variable, and partition those constraints into groups that share a common type variable a.
In this case, there's only one group: (Enum a, Num a).
Keep only the groups in which at least one of the classes is an interactive class (defined below).
This group is kept, because Num is an interactive class.
Now, for each remaining group G, try each type ty from the default-type list in turn; if setting a = ty would allow the constraints in G to be completely solved. If so, default a to ty.
The unit type () and the list type [] are added to the start of the standard list of types which are tried when doing type defaulting.
The default default-type list (sic) is (with the additions from the last clause) default ((), [], Integer, Double).
So when you do Prelude> succ i to actually evaluate this expression (note :t doesn't evaluate the expression it gets), a is set to Integer (first of this list satisfying the constraints), and the result is printed as 2.
You can see it's the reason by changing the default:
Prelude> default (Double)
Prelude> succ 1
2.0
For the updated question:
I'm now starting to realise that my question must stem from a wrong assumption: 'that once i is inferred to be i :: Num a => a, then i can be nothing else'. Hence I was puzzled to see succ i was evaluated without errors.
i can be nothing else (i.e. nothing that doesn't fit this type), but it can be used with less general (more specific) types: Integer, Int. Even with many of them in an expression at once:
Prelude> (i :: Double) ^ (i :: Integer)
1.0
And these uses don't affect the type of i itself: it's already defined and its type fixed. OK so far?
Well, adding constraints also makes the type more specific, so (Num a, Enum a) => a is more specific than (Num a) => a:
Prelude> i :: (Num a, Enum a) => a
1
Because of course any type a that satisfies both constraints in (Num a, Enum a) satisfies just Num a.
However it is not adding Enum a when the type variable appears as a function:
That's because you specified a signature which doesn't allow it to. If you don't give a signature, there's no reason to infer Num constraint. But e.g.
Prelude> f x = succ x + 1
will infer the type with both constraints:
Prelude> :t f
f :: (Num a, Enum a) => a -> a
So it seems that inference rules for functions are stricter than the ones for variables?
It's actually the other way around due to the monomorphism restriction (not in GHCi, by default). You've actually been a bit lucky not to run into it here, but the answer is already long enough. Searching for the term should give you explanations.
GHC is saying my_succ :: forall a. Num a => a -> a and given forall a doesn't appear in the type-signature of neither i nor x.
That's a red herring. I am not sure why it's shown in one case and not the other, but all of them have that forall a behind the scenes:
Haskell type signatures are implicitly quantified. When the language option ExplicitForAll is used, the keyword forall allows us to say exactly what this means. For example:
g :: b -> b
means this:
g :: forall b. (b -> b)
(Also, you just need ExplicitForAll and not RankNTypes to write down forall a. Num a => a.)

types and type variable in Haskell

Scratching at the surface of Haskell's type system, ran this:
Prelude> e = []
Prelude> ec = tail "a"
Prelude> en = tail [1]
Prelude> :t e
e :: [a]
Prelude> :t ec
ec :: [Char]
Prelude> :t en
en :: Num a => [a]
Prelude> en == e
True
Prelude> ec == e
True
Somehow, despite en and ec have different types, they both test True on == e. I say "somehow" not because I am surprised (I am not), but because I don't know what is the name of rule/mechanism that allows this. It is as if the type variable "a" in the expression "[] == en" is allowed to take on value of "Num" for the evaluation. And likewise when tested with "[] == ec", it is allowed to become "Char".
The reason I'm not sure my interpretation is correct is this:
Prelude> (en == e) && (ec == e)
True
, because intuitively this implies that in the same expression e assumes both values of Num and Char "at the same time" (at least that's how I'm used to interpret the semantics of &&). Unless the "assumption" of Char only acts during the evaluation of (ec == e), and (en == e) is evaluated independently, in a separate... reduction? (I'm guessing on a terminology here).
And then comes this:
Prelude> en == es
<interactive>:80:1: error:
• No instance for (Num Char) arising from a use of ‘en’
• In the first argument of ‘(==)’, namely ‘en’
In the expression: en == es
In an equation for ‘it’: it = en == es
Prelude> es == en
<interactive>:81:7: error:
• No instance for (Num Char) arising from a use of ‘en’
• In the second argument of ‘(==)’, namely ‘en’
In the expression: es == en
In an equation for ‘it’: it = es == en
Not surprise by the exception, but surprised that in both tests, the error message complains about "the use of 'en'" - and doesn't matter if it's the first or second operand.
Perhaps an important lesson needs to be learned about Haskell type system. Thank you for your time!
When we say that e :: [a], it means that e is a list of elements of any type. Which type? Any type! Whichever type you happen to need at the moment.
If you're coming from a non-ML language, this might be a bit easier to understand by looking at a function (rather than a value) first. Consider this:
f x = [x]
The type of this function is f :: a -> [a]. This means, roughly, that this function works for any type a. You give it a value of this type, and it will give you back a list with elements of that type. Which type? Any type! Whichever you happen to need.
When I call this function, I effectively choose which type I want at the moment. If I call it like f 'x', I choose a = Char, and if I call it like f True, I choose a = Bool. So the important point here is that whoever calls a function chooses the type parameter.
But I don't have to choose it just once and for all eternity. Instead, I choose the type parameter every time I call the function. Consider this:
pair = (f 'x', f True)
Here I'm calling f twice, and I choose different type parameters every time - first time I choose a = Char, and second time I choose a = Bool.
Ok, now for the next step: when I choose the type parameter, I can do it in several ways. In the example above, I choose it by passing a value parameter of the type I want. But another way is to specify the type of result I want. Consider this:
g x = []
a :: [Int]
a = g 0
b :: [Char]
b = g 42
Here, the function g ignores its parameter, so there is no relation between its type and the result of g. But I am still able to choose the type of that result by having it constrained by the surrounding context.
And now, the mental leap: a function without any parameters (aka a "value") is not that different from a function with parameters. It just has zero parameters, that's all.
If a value has type parameters (like your value e for example), I can choose that type parameter every time I "call" that value, just as easily as if it was a function. So in the expression e == ec && e == en you're simply "calling" the value e twice, choosing different type parameters on every call - much like I've done in the pair example above.
The confusion about Num is an altogether different matter.
You see, Num is not a type. It's a type class. Type classes are sort of like interfaces in Java or C#, except you can declare them later, not necessarily together with the type that implements them.
So the signature en :: Num a => [a] means that en is a list with elements of any type, as long as that type implements ("has an instance of") the type class Num.
And the way type inference in Haskell works is, the compiler will first determine the most concrete types it can, and then try to find implementations ("instances") of the required type classes for those types.
In your case, the compiler sees that en :: [a] is being compared to ec :: [Char], and it figures: "oh, I know: a must be Char!" And then it goes to find the class instances and notices that a must have an instance of Num, and since a is Char, it follows that Char must have an instance of Num. But it doesn't, and so the compiler complains: "can't find (Num Char)"
As for "arising from the use of en" - well, that's because en is the reason that a Num instance is required. en is the one that has Num in its type signature, so its presence is what causes the requirement of Num
Sometimes, it is convenient to think about polymorphic functions as functions taking explicit type arguments. Let's consider the polymorphic identity function as an example.
id :: forall a . a -> a
id x = x
We can think of this function as follows:
first, the function takes as input a type argument named a
second, the function takes as input a value x of the previously chosen type a
last, the function returns x (of type a)
Here's a possible call:
id #Bool True
Above, the #Bool syntax passes Bool for the first argument (type argument a), while True is passed as the second argument (x of type a = Bool).
A few other ones:
id #Int 42
id #String "hello"
id #(Int, Bool) (3, True)
We can even partially apply id passing only the type argument:
id #Int :: Int -> Int
id #String :: String -> String
...
Now, note that in most cases Haskell allows us to omit the type argument. I.e. we can write id "hello" and GHC will try to infer the missing type argument. Roughly it works as follows: id "hello" is transformed into id #t "hello" for some unknown type t, then according to the type of id this call can only type check if "hello" :: t, and since "hello" :: String, we can infer t = String.
Type inference is extremely common in Haskell. Programmers rarely specify their type arguments, and let GHC do its job.
In your case:
e :: forall a . [a]
e = []
ec :: [Char]
ec = tail "1"
en :: [Int]
en = tail [1]
Variable e is bound to a polymorphic value. That is, it actually is a sort-of function which takes a type argument a (which can also be omitted), and returns a list of type [a].
Instead, ec does not take any type argument. It's a plain list of type [Char]. Similarly for en.
We can then use
ec == (e #Char) -- both of type [Char]
en == (e #Int) -- both of type [Int]
Or we can let the type inference engine to determine the implicit type arguments
ec == e -- #Char inferred
en == e -- #Int inferred
The latter can be misleading, since it seems that ec,e,en must have the same type. In fact, they have not, since different implicit type arguments are being inferred.

defining an instance vs using class constraints

I'm going through Learn You a Haskell
and am on the chapter that discusses typeclasses. There is this snippet:
class (Eq a) => Num a where
I understood this as a class constraint being imposed on the Num class, forcing it to act as an equatable class. My question is, why wasn't Num implemented as an instance of Eq? Like this:
instance Eq Num where
... stuff
Defining an instance seems like a cleaner way to do this, but maybe I'm missing something. Can someone please explain the difference to me?
Let's pronounce a couple of these Haskell declarations in English; perhaps that will clarify a few things. I'll give the Haskell declaration first, and an English transliteration afterwards.
class Eq a where (==) :: a -> a -> Bool
There is a set of types. We name this set Eq. If a is a type in this set, then we can compare two values of that type for equality using a function named (==).
data Brillant = T | F | FileNotFound
instance Eq Brillant where
T == T = True
F == F = True
FileNotFound == FileNotFound = True
_ == _ = False
There is a type named Brillant. It has constructors T, F, and FileNotFound. The Brillant type is an element of the Eq set. Two terms of type Brillant can be compared for equality by checking whether they use the same constructor.
data Maybe a = Nothing | Just a
instance Eq a => Eq (Maybe a) where
Nothing == Nothing = True
Just v == Just v' = v == v'
Whenever we have a type a, there is another type Maybe a. If a is an element of the Eq set, then Maybe a is an element of the Eq set.
class Eq a => Num a where
(+) :: a -> a -> a
There is a set of types. We name this set Num. If a is a type in this set, then a is also in the set Eq (or, in other words, Num is a subset of Eq). If a is a type in the Num set, then we can add two values of this type and get a third using the (+) function.
If the following declaration were valid Haskell, here's what it would mean:
instance Eq Num where -- ...
The set named Num of types is an element of the set named Eq of types.
Now let's compare the last two, namely, class Eq a => Num a and instance Eq Num. The former says that members of the Num set are also members of the Eq set, while the latter says that the Num set itself is a member of the Eq set. Hopefully the latter sets off some kind of alarm bell in your head. Since we said Eq is a set of types, all members of that set should be types. But we also said Num is a set of types, not a type; so it can't be a member.
It can make sense to talk about sets of sets of types -- that is, classes whose instances are other classes -- but neither Eq nor Num do that. So it would not be correct to claim that Num is an instance of Eq.

Why doesn't f=(+) need a type annotation?

I mean, for example,
f :: (Enum a) => a -> a --without this line, there would be an error
f = succ
It's because succ needs its parameter to be enumerable (succ :: (Enum a) => a -> a)
but for (+)
f = (+) --ok
Though (+)'s declaration is (+) :: (Num a) => a –> a –> a.
I mean, why don't I need to declare f as f :: (Num a) => a –> a –> a?
Because of defaulting. Num is a 'defaultable' type class, meaning that if you leave it un-constrained, the compiler will make a few intelligent guesses as to which type you meant to use it as. Try putting that definition in a module, then running
:t f
in ghci; it should tell you (IIRC) f :: Integer -> Integer -> Integer. The compiler didn't know which a you wanted to use, so it guessed Integer; and since that worked, it went with that guess.
Why didn't it infer a polymorphic type for f? Because of the dreaded[1] monomorphism restriction. When the compiler sees
f = (+)
it thinks 'f is a value', which means it needs a single (monomorphic) type. Eta-expand the definition to
f x = (+) x
and you will get the polymorphic type
f :: Num a => a -> a -> a
and similarly if you eta-expand your first definition
f x = succ x
you don't need a type signature any more.
[1] Actual name from the GHC documentation!
I mean, why don't I need to declare f as (+) :: (Num a) => a –> a –> a?
You do need to do that, if you declare the signature of f at all. But if you don't, the compiler will “guess” the signature itself – in this case this isn't all to remarkable since it can basically just copy&paste the signature of (+). And that's precisely what it will do.
...or at least what it should do. It does, provided you have the -XNoMonomorphism flag on. Otherwise, well, the dreaded monomorphism restriction steps in because f's definition is of the shape ConstantApplicativeForm = Value; that makes the compiler dumb down the signature to the next best non-polymorphic type it can find, namely Integer -> Integer -> Integer. To prevent this, you should in fact supply the right signature by hand, for all top-level functions. That also prevents a lot of confusion, and many errors become way less confusing.
The monomorphism restriction is the reason
f = succ
won't work on its own: because it also has this CAF shape, the compiler does not try to infer the correct polymorphic type, but tries to find some concrete instantiation to make a monomorphic signature. But unlike Num, the Enum class does not offer a default instance.
Possible solutions, ordered by preference:
Always add signatures. You really should.
Enable -XNoMonomorphismRestriction.
Write your function definitions in the form f a = succ a, f a b = a+b. Because there are explicitly mentioned arguments, these don't qualify as CAF, so the monomorphism restriction won't kick in.
Haskell defaults Num constraints to Int or Integer, I forget which.

Resources