Why can I call a function from a typeclass instance directly in the REPL (like compare from Ord)? - haskell

When I am in a REPL like GHCI with Prelude, and I write
*> compare 5 7
LT
Why can I call that function (compare) like that directly in the REPL?
I know that compare is defined in typeclass Ord. The typeclass definition for Ord of course shows that it is a subclass of Eq.
Here is my line of reasoning:
5 has type Num a => a, and Num typeclass is not a subclass of Eq.
Also,
Prelude> :t (compare 5)
(compare 5) :: (Num a, Ord a) => a -> Ordering
So, there is an additional constraint imposed here when I apply a numeric type argument. when I call compare 5 7, the types of the arguments are narrowed to something that does have an instance of Ord. I think the narrowing happens to the default concrete type associated with the typeclass: in the case of Num, this is Integer, which has an instance of Real, which has an instance of Ord.
However, coming from a non-functional programming background, I would have imagined that I would have to call compare on one of the numbers (like calling it on an object in OOP). If 5 is Integer, which does implement Ord, then why do I call compare in the REPL itself? This is obviously a question related to a paradigm shift for me and I still didn't get it. Hopefully someone can explain.

The type defaulting here comes into play. The interpreter can derive that 5 and 7 need to be of the same type, and members of the Ord and Num typeclass. The default for a Num is Integer, and since Integer is an instance of Ord as well, we can thus use Integer.
The interpreter thus considers 5 and 7 to be Integers here in that case, and thus it can evaluate the function and obtain LT.
GHCi has some additional defaulting rules, described in the GHCi documentation.

Methods like compare are associated with types, not particular values. The compiler needs to be able to deduce the type in order to select the correct typeclass instance, but that doesn't require any special assistance.
The type of compare is
compare :: (Ord a) => a -> a -> Ordering
Thus any of its arguments (of type a) can be used to look up the Ord instance.
As you correctly assumed, in the compare 5 7 example, the types of 5 and 7 default to Integer. Thus a in the compare type is deduced to be Integer and the Ord Integer instance is selected.
This selection does not necessarily go through a function argument. Consider e.g.
read :: (Read a) => String -> a
Here it is the result type that drives instance selection, but the type checker is just fine with it:
> read "(2, 3)" :: (Int, Int)
(2,3)
(What would the OO equivalent be? "(2, 3)".read()?)
In fact, methods don't even have to be functions:
maxBound :: (Bounded a) => a
This is a polymorphic value, not a function:
> maxBound :: Int
9223372036854775807
Class instances are uniquely connected to types, so as long as the type checker has enough information to figure out what that type variable represents, everything works out. That is, in
someMethod :: (SomeClass foo) => ...
foo has to appear somewhere in the type signature ... so the type checker can resolve SomeClass foo from the way someMethod is used at any given point (at least in the absence of certain language extensions).

Related

Confusion with Haskell classes

I am confused with classes in Haskell as follows.
I can define a function that takes an Integral argument, and successfully supply it with a Num argument:
gi :: Integral a => a -> a
gi i = i
gin = gi (3 :: Num a => a)
I can define a function that takes a Num argument, and successfully supply it with an Integral argument:
fn :: Num a => a -> a
fn n = n
fni = fn (3 :: Integral a => a)
I can define an Integral value and assign a Num to it
i :: Integral a => a
i = (3 :: Num a => a)
But if I try to define a Num value, then I get a parse error if I assign an Integral value to it
- this doesn't work
n :: Num a => a
n = (3 :: Integral a => a)
Maybe I am being confused by my OO background. But why do function variables appear to let you go 'both ways' i.e. can provide a value of a subclass when a superclass is 'expected' and can provide a value of a superclass when a subclass is expected, whereas in value assignment you can provide a superclass to a subclass value but can't assign a subclass to a superclass value?
For comparison, in OO programming you can typically assign a child value to a parent type, but not vice-versa. In Haskell, the opposite appears to be the case in the second pair of examples.
The first two examples don't actually have anything to do with the relationship between Num and Integral.
Take a look at the type of gin and fni. Let's do it together:
> :t gin
gin :: Integer
> :t fni
fni :: Integer
What's going on? This is called "type defaulting".
Technically speaking, any numeric literal like 3 or 5 or 42 in Haskell has type Num a => a. So if you wanted it to just be an integer number dammit, you'd have to always write 42 :: Integer instead of just 42. This is mighty inconvenient.
So to work around that, Haskell has certain rules that in certain special cases prescribe concrete types to be substituted when the type comes out generic. And in case of both Num and Integral the default type is Integer.
So when the compiler sees 3, and it's used as a parameter for gi, the compiler defaults to Integer. That's it. Your additional constraint of Num a has no further effect, because Integer is, in fact, already an instance of Num.
With the last two examples, on the other hand, the difference is that you explicitly specified the type signature. You didn't just leave it to the compiler to decide, no! You specifically said that n :: Num a => a. So the compiler can't decide that n :: Integer anymore. It has to be generic.
And since it's generic, and constrained to be Num, an Integral type doesn't work, because, as you have correctly noted, Num is not a subclass of Integral.
You can verify this by giving fni a type signature:
-- no longer works
fni :: Num a => a
fni = fn (3 :: Integral a => a)
Wait, but shouldn't n still work? After all, in OO this would work just fine. Take C#:
class Num {}
class Integral : Num {}
class Integer : Integral {}
Num a = (Integer)3
// ^ this is valid (modulo pseudocode), because `Integer` is a subclass of `Num`
Ah, but this is not a generic type! In the above example, a is a value of a concrete type Num, whereas in your Haskell code a is itself a type, but constrained to be Num. This is more like a C# interface than a C# class.
And generic types (whether in Haskell or not) actually work the other way around! Take a value like this:
x :: a
x = ...
What this type signature says is that "Whoever has a need of x, come and take it! But first name a type a. Then the value x will be of that type. Whichever type you name, that's what x will be"
Or, in plainer terms, it's the caller of a function (or consumer of a value) that chooses generic types, not the implementer.
And so, if you say that n :: Num a => a, it means that value n must be able to "morph" into any type a whatsoever, as long as that type has a Num instance. Whoever will use n in their computation - that person will choose what a is. You, the implementer of n, don't get to choose that.
And since you don't get to choose what a is, you don't get to narrow it down to be not just any Num, but an Integral. Because, you know, there are some Nums that are not Integrals, and so what are you going to do if whoever uses n chooses one of those non-Integral types to be a?
In case of i this works fine, because every Integral must also be Num, and so whatever the consumer of i chooses for a, you know for sure that it's going to be Num.

The type-specification operator is like down-casting in Object-Oriented languages?

I was going through the book Haskell Programming from First Principles and came across following code-snippet.
Prelude> fifteen = 15
Prelude> :t fifteen
fifteen :: Num a => a
Prelude> fifteenInt = fifteen :: Int
Prelude> fifteenDouble = fifteen :: Double
Prelude> :t fifteenInt
fifteenInt :: Int
Prelude> :t fifteenDouble
fifteenInt :: Double
Here, Num is the type-class that is like the base class in OO languages. What I mean is when I write a polymorphic function, I take a type variable that is constrained by Num type class. However, as seen above, casting fifteen as Int or Double works. Isn't it equivalent to down-casting in OO languages?
Wouldn't some more information (a bunch of Double type specific functions in this case) be required for me to be able to do that?
Thanks for helping me out.
No, it's not equivalent. Downcasting in OO is a runtime operation: you have a value whose concrete type you don't know, and you basically assert that it has some particular case – which is an error if it happens to be actually a different concrete type.
In Haskell, :: isn't really an operator at all. It just adds extra information to the typechecker at compile-time. I.e. if it compiles at all, you can always be sure that it will actually work at runtime.
The reason it works at all is that fifteen has no concrete type. It's like a template / generic in OO languages. So when you add the :: Double constraint, the compiler can then pick what type is instantiated for a. And Double is ok because it is a member of the Num typeclass, but don't confuse a typeclass with an OO class: an OO class specifies one concrete type, which may however have subtypes. In Haskell, subtypes don't exist, and a class is more like an interface in OO languages. You can also think of a typeclass as a set of types, and fifteen has potentially all of the types in the Num class; which one of these is actually used can be chosen with a signature.
Downcasting is not a good analogy. Rather, compare to generic functions.
Very roughly, you can pretend that your fifteen is a generic function
// pseudo code in OOP
A fifteen<A>() where A : Num
When you use fifteen :: Double in Haskell, you tell the compiler that the result of the above function is Double, and that enables the compiler to "call" the above OOP function as fifteen<Double>(), inferring the generic argument.
With some extension on, GHC Haskell has a more direct way to choose the generic parameter, namely the type application fifteen #Double.
There is a difference between the two ways in that ... :: Double specifies what is the return type, while #Double specifies what is the generic argument. In this fifteen case they are the same, but this is not always the case. For instance:
> list = [(15, True)]
> :t list
list :: Num a => [(a, Bool)]
Here, to choose a = Double, we need to write either list :: [(Double, Bool)] or list #Double.
In the type forall a. Num a => a†, the forall a and Num a are parameters specified by the “caller”, that is, the place where the definition (fifteen) used. The type parameter is implicitly filled in with a type argument by GHC during type inference; the Num constraint becomes an extra parameter, a “dictionary” comprising a record of functions ((+), (-), abs, &c.) for a particular Num instance, and which Num dictionary to pass in is determined from the type. The type argument exists only at compile time, and the dictionary is then typically inlined to specialise the function and enable further optimisations, so neither of these parameters typically has any runtime representation.
So in fifteen :: Double, the compiler deduces that a must be equal to Double, giving (a ~ Double, Num a) => a, which is simplified first to Num Double => Double, then to simply Double, because the constraint Num Double is satisfied by the existence of an instance Num Double definition. There is no subtyping or runtime downcasting going on, only the solution of equality constraints, statically.
The type argument can also be specified explicitly with the TypeApplications syntax of fifteen #Double, typically written like fifteen<Double> in OO languages.
The inferred type of fifteen includes a Num constraint because the literal 15 is implicitly a call to something like fromInteger (15 :: Integer)‡. fromInteger has the type Num a => Integer -> a and is a method of the Num typeclass, so you can think of a literal as “partially applying” the Integer argument while leaving the Num a argument unspecified, then the caller decides which concrete type to supply for a, and the compiler inserts a call to the fromInteger function in the Num dictionary passed in for that type.
† forall quantifiers are typically implicit, but can be written explicitly with various extensions, such as ExplicitForAll, ScopedTypeVariables, and RankNTypes.
‡ I say “something like” because this abuses the notation 15 :: Integer to denote a literal Integer, not circularly defined in terms of fromInteger again. (Else it would loop: fromInteger 15 = fromInteger (fromInteger 15) = fromInteger (fromInteger (fromInteger 15))…) This desugaring can be “magic” because it’s a part of the language itself, not something defined within the language.

The meaning of <= vs. => in Haskell

I'm relatively new to Haskell; what does the <= syntax represent and what is the difference between <= and =>? Examples of both would be helpful.
The two are completely unrelated; they just seem related because of ASCII. It makes more sense if you look at their Unicode equivalents:
=> is an arrow: ⇒. It's used to specify constraints in type signatures:
Eq a => a -> a -> Bool
The Eq a => in the above signature means that the type variable a can be any type that is an instance of the Eq class. That is, any type that either has deriving (Eq) or an explicit instance like instance Eq Type where ....
In function signatures, -> specifies a normal argument while => specifies constraints in the signature. In the above example (Eq a => a -> a -> Bool), the function takes two arguments of type a and gives us a Bool. The Eq a => part is not an explicit argument to the function; it just tells us that a must be part of Eq (that is, it must be comparable with ==).
<= is less than or equal to. That is, it's ≤, not ⇐. It's a normal function in the standard library that's part of the Ord class:
λ> :t (<=)
(<=) :: Ord a => a -> a -> Bool
You can use it in a normal expression:
λ> 10 <= 12
True
The only reason they seem symmetrical is because the ASCII approximation of ≤ and ⇐ are the same, but that's just a limitation in notation. Otherwise, they're completely unrelated.
You can use the unambiguous Unicode symbols in your code. The UnicodeSyntax extension enables using ⇒ for => and the base-unicode-symbols package contains Unicode versions of standard library functions including ≤ for <=.
They're completely different things.
=> (as well as ->) is built-in type-level syntax. It's used for denoting constraints. For instance, the signature
abs :: Num a => a -> a
tells you that the abs function takes a value of type a and yields a value of the same type, under the condition a is a number type (i.e., fulfills the Num a constraint). Such a constraint will usually be† a type class; in this case
class Num a where
...
You can read the => arrow as a sort-of function mapping, too: abs first takes the information of what specific sort of number a is as an “implicit argument”, then one such number as an explicit argument, and only then gives the result.
<= is not syntax, it's just an infix operator that's defined in the standard library. Specifically it's the less-than operator, which mathematicians write &leq;.You can look up such operators on Hayoo, no need to ask questions about them.
†Strictly speaking, a class is not a constraint but a “constraint constructor”, i.e. a type-level function whose result is of kind Constraint. For instance, Num :: * -> Constraint applied to e.g. Int :: * means that Num Int is a constraint. (Those are not type signatures but kind signatures, i.e. “types of type-level things”.)

What is Ord type?

Is every class not a type in Haskell :
Prelude> :t max
max :: Ord a => a -> a -> a
Prelude> :t Ord
<interactive>:1:1: Not in scope: data constructor ‘Ord’
Prelude>
Why does this not print Ord type signature ?
Okay, there's a couple of things going on here.
First when you write :t Ord you're looking for something called Ord in the value namespace; specifically it would have to be a constructor, since the name starts with a capital letter.
Haskell keeps types and values completely separate; there is no relationship between the name of a type and the names of a type's constructors. Often when there's only one constructor, people will use the same name as the type. An example being data Foo = Foo Int. This declares two new named entities: the type Foo and the constructor Foo :: Int -> Foo.
It's not really a good idea to think of it as just making a type Foo that can be used both in type expressions and to construct Foos. Because also common are declarations like data Maybe a = Nothing | Just a. Here there are 2 different constructors for Maybe a, and Maybe isn't a name of anything at all at the value level.
So just because you've seen Ord in a type expression doesn't mean that there is a name Ord at the value level for you to ask the type of with :t. Even if there were, it wouldn't necessarily be related top the type-level name Ord.
The second point that needs clarifying is that no, classes are not in fact types. A class is a set of types (which all support the interface defined in the class), but it is not a type itself.
In vanilla Haskell type classes are just "extra" things. You can declare them with a class declaration, instantiate them with an instance declaration, and use them in special syntax attached to types (the stuff left of the => arrow) as constraints on type variables. But they don't really interact with the rest of the language, and you cannot use them in the main part of a type signature (the stuff right of the `=> arrow).
However, with the ConstraintKinds extension on, type classes do become ordinary things that exist in the type namespace, just like Maybe. They are still not types in the sense that there can never be any values that have them as types, so you can't use Ord or Ord Int as an argument or return type in a function, or have a [Ord a] or anything like that.
In that they are a bit like type constructors like Maybe. Maybe is a name bound in the type namespace, but it is not a type as such; there are no values whose type is just Maybe, but Maybe can be used as part of an expression defining a type, as in Maybe Int.
If you're not familiar with kinds, probably ignore everything I've said from ConstraintKinds onwards; you'll probably learn about kinds eventually, but they're not a feature you need to know much about as a beginner. If you are, however, what ConstraintKinds does is make a special kind Constraint and have type class constraints (left of the => arrow) just be ordinary type-level things of kind Constraint instead of special purpose syntax. This means that Ord is a type-level thing, and we can ask it's kind with the :k command in GHCI:
Prelude> :k Ord
* -> Constraint
Which makes sense; max had type Ord a => a -> a -> a, so Ord a must have kind Constraint. If Ord can be applied to an ordinary type to yield a constraint, it must have kind * -> Constraint.
Ord isn't a type; it's a typeclass. Typeclasses allow you to associate supported operations with a given type (somewhat similar to interfaces in Java or protocols in Objective-C). A type (e.g. Int) being an "instance" of a typeclass (e.g. Ord) means that the type supports the functions of the Ord typeclass (e.g. compare, <, > etc.).
You can get most info about a typeclass using :i in ghci, which shows you the functions associated with the typeclass and which types are instances of it:
ghci > :i Ord
class Eq a => Ord a where
compare :: a -> a -> Ordering
(<) :: a -> a -> Bool
(>=) :: a -> a -> Bool
(>) :: a -> a -> Bool
(<=) :: a -> a -> Bool
max :: a -> a -> a
min :: a -> a -> a
-- Defined in ‘GHC.Classes’
instance Ord a => Ord (Maybe a) -- Defined in ‘Data.Maybe’
instance (Ord a, Ord b) => Ord (Either a b)
-- Defined in ‘Data.Either’
instance Ord Integer -- Defined in ‘integer-gmp:GHC.Integer.Type’
instance Ord a => Ord [a] -- Defined in ‘GHC.Classes’
...
Ord is not a type, but a typeclass. It does not have a type, but a kind:
Prelude> :k Ord
Ord :: * -> Constraint
Typeclasses are one of the wonderful things about Haskell. Check 'em out :-)
Not quite. You can impose type constraints, so Ord a => a is a type, but Ord a isn't. Ord a => a means "any type a with the constraint that it is an instance of Ord".
The error is because :t expects an expression. When GHCi tries to interpret Ord as an expression, the closest it can get to is a data constructor, since these are the only functions in Haskell that can start with capital letters.

Polymorphic signature for non-polymorphic function: why not?

As an example, consider the trivial function
f :: (Integral b) => a -> b
f x = 3 :: Int
GHC complains that it cannot deduce (b ~ Int). The definition matches the signature in the sense that it returns something that is Integral (namely an Int). Why would/should GHC force me to use a more specific type signature?
Thanks
Type variables in Haskell are universally quantified, so Integral b => b doesn't just mean some Integral type, it means any Integral type. In other words, the caller gets to pick which concrete types should be used. Therefore, it is obviously a type error for the function to always return an Int when the type signature says I should be able to choose any Integral type, e.g. Integer or Word64.
There are extensions which allow you to use existentially quantified type variables, but they are more cumbersome to work with, since they require a wrapper type (in order to store the type class dictionary). Most of the time, it is best to avoid them. But if you did want to use existential types, it would look something like this:
{-# LANGUAGE ExistentialQuantification #-}
data SomeIntegral = forall a. Integral a => SomeIntegral a
f :: a -> SomeIntegral
f x = SomeIntegral (3 :: Int)
Code using this function would then have to be polymorphic enough to work with any Integral type. We also have to pattern match using case instead of let to keep GHC's brain from exploding.
> case f True of SomeIntegral x -> toInteger x
3
> :t toInteger
toInteger :: Integral a => a -> Integer
In the above example, you can think of x as having the type exists b. Integral b => b, i.e. some unknown Integral type.
The most general type of your function is
f :: a -> Int
With a type annotation, you can only demand that you want a more specific type, for example
f :: Bool -> Int
but you cannot declare a less specific type.
The Haskell type system does not allow you to make promises that are not warranted by your code.
As others have said, in Haskell if a function returns a result of type x, that means that the caller gets to decide what the actual type is. Not the function itself. In other words, the function must be able to return any possible type matching the signature.
This is different to most OOP languages, where a signature like this would mean that the function gets to choose what it returns. Apparently this confuses a few people...

Resources