How to apply polymorphic function to a concrete type? - haskell

Below is a distilled version of a problem I encountered while learning Haskell:
data A = A
data B = B
data Test = TestA A
| TestB B
test :: (a -> a) -> (Test -> Test)
test op t =
case t of
TestA a -> TestA $ op a
TestB b -> TestB $ op b
testA = test id (TestA A)
testB = test id (TestB B)
Trying to compile this gives the following error:
Couldn't match expected type ‘B’ with actual type ‘a’
‘a’ is a rigid type variable bound by
the type signature for test :: (a -> a) -> Test -> Test
What's going on? I thought that when I pass in a polymorphic function, I should be able to apply it to values of different concrete types.

The basic problem here is how Haskell infers quantification from free variables in type signatures. Given the following type signature...
test :: (a -> a) -> (Test -> Test)
...the type variable a is unbound. Haskell automatically converts unbound type variables into universal quantification constraints, so the above type is actually interpreted like this:
test :: forall a. (a -> a) -> (Test -> Test)
Now the error you are getting might make a little bit more sense—the type variable a can only unify with one type per invocation of test, which is decided by the caller. Therefore, the (a -> a) function could be String -> String or Int -> Int or any other type, but it can never be a function that works on both A and B.
Obviously, though, you had a different intent when you wrote that type signature. You wanted the (a -> a) function to be a type signature like the one for id: a function that truly works for any value, not some particular function for some particular choice of a. To specify this, you must make the forall explicit so that the compiler knows precisely how that type variable should be quantified:
test :: (forall a. a -> a) -> (Test -> Test)
However, the above type is actually not valid in standard Haskell. It is supported by GHC, though, by using the Rank2Types or RankNTypes extension, which permits “higher rank” polymorphism like the type signature above.

Related

SystemT Compiler and dealing with Infinite Types in Haskell

I'm following this blog post: http://semantic-domain.blogspot.com/2012/12/total-functional-programming-in-partial.html
It shows a small OCaml compiler program for System T (a simple total functional language).
The entire pipeline takes OCaml syntax (via Camlp4 metaprogramming) transforms them to OCaml AST that is translated to SystemT Lambda Calculus (see: module Term) and then finally SystemT Combinator Calculus (see:
module Goedel). The final step is also wrapped with OCaml metaprogramming Ast.expr type.
I'm attempting to translate it to Haskell without the use of Template Haskell.
For the SystemT Combinator form, I've written it as
{-# LANGUAGE GADTs #-}
data TNat = Zero | Succ TNat
data THom a b where
Id :: THom a a
Unit :: THom a ()
ZeroH :: THom () TNat
SuccH :: THom TNat TNat
Compose :: THom a b -> THom b c -> THom a c
Pair :: THom a b -> THom a c -> THom a (b, c)
Fst :: THom (a, b) a
Snd :: THom (a, b) b
Curry :: THom (a, b) c -> THom a (b -> c)
Eval :: THom ((a -> b), a) b -- (A = B) * A -> B
Iter :: THom a b -> THom (a, b) b -> THom (a, TNat) b
Note that Compose is forward composition, which differs from (.).
During the translation of SystemT Lambda Calculus to SystemT Combinator Calculus, the Elaborate.synth function tries to convert SystemT Lambda calculus variables into series of composed projection expressions (related to De Brujin Indices). This is because combinator calculus doesn't have variables/variable names. This is done with the Elaborate.lookup which uses the Quote.find function.
The problem is that with my encoding of the combinator calculus as the GADT THom a b. Translating the Quote.find function:
let rec find x = function
| [] -> raise Not_found
| (x', t) :: ctx' when x = x' -> <:expr< Goedel.snd >>
| (x', t) :: ctx' -> <:expr< Goedel.compose Goedel.fst $find x ctx'$ >>
Into Haskell:
find :: TVar -> Context -> _
find tvar [] = error "Not Found"
find tvar ((tvar', ttype):ctx)
| tvar == tvar' = Snd
| otherwise = Compose Fst (find tvar ctx)
Results in an infinite type error.
• Occurs check: cannot construct the infinite type: a ~ (a, c)
Expected type: THom (a, c) c
Actual type: THom ((a, c), c) c
The problem stems from the fact that using Compose and Fst and Snd from the THom a b GADT can result in infinite variations of the type signature.
The article doesn't have this problem because it appears to use the Ast.expr OCaml thing to wrap the underlying expressions.
I'm not sure how to resolve in Haskell. Should I be using an existentially quantified type like
data TExpr = forall a b. TExpr (THom a b)
Or some sort of type-level Fix to adapt the infinite type problem. However I'm unsure how this changes the find or lookup functions.
This answer will have to be a bit high-level, because there are three entirely different families of possible designs to fix that problem. What you’re doing seems closer to approach three, but the approaches are ordered by increasing complexity.
The approach in the original post
Translating the original post requires Template Haskell and partiality; find would return a Q.Exp representing some Hom a b, avoiding this problem just like the original post. Just like in the original post, a type error in the original code would be caught when typechecking the output of all the Template Haskell functions. So, type errors are still caught before runtime, but you will still need to write tests to find cases where your macros output ill-typed expressions. One can give stronger guarantees, at the cost of a significant increase in complexity.
Dependent typing/GADTs in input and output
If you want to diverge from the post, one alternative is to use “dependent typing” throughout and make the input dependently-typed. I use the term loosely to include both actually dependently-typed languages, actual Dependent Haskell (when it lands), and ways to fake dependent typing in Haskell today (via GADTs, singletons, and what not).
However, you lose the ability to write your own typechecker and choose which type system to use; typically, you end up embedding a simply-typed lambda calculus, and can simulate polymorphism via polymorphic Haskell functions that can generate terms at a given type. This is easier in dependently-typed languages, but possible at all in Haskell.
But honestly, in this road it’s easier to use higher-order abstract syntax and Haskell functions, with something like:
data Exp a where
Abs :: (Exp a -> Exp b) -> Exp (a -> b)
App :: Exp (a -> b) -> Exp a -> Exp b
Var :: String -> Exp a —- only use for free variables
exampleId :: Exp (a -> a)
exampleId = Abs (\x -> x)
If you can use this approach (details omitted here), you get high assurance from GADTs with limited complexity. However, this approach is too inflexible for many scenarios, for instance because the typing contexts are only visible to the Haskell compiler and not in your types or terms.
From untyped to typed terms
A third option is go dependently-typed and to still make your program turn weakly-typed input to strongly typed output. In this case, your typechecker overall transforms terms of some type Expr into terms of a GADT TExp gamma t, Hom a b, or such. Since you don’t know statically what gamma and t (or a and b) are, you’ll indeed need some sort of existential.
But a plain existential is too weak: to build bigger well-typed expression, you’ll need to check that the produced types allow it. For instance, to build a TExpr containing a Compose expression out of two smaller TExpr, you'll need to check (at runtime) that their types match. And with a plain existential, you can't. So you’ll need to have a representation of types also at the value level.
What's more existentials are annoying to deal with (as usual), since you can’t ever expose the hidden type variables in your return type, or project those out (unlike dependent records/sigma-types).
I am honestly not entirely sure this could eventually be made to work. Here is a possible partial sketch in Haskell, up to one case of find.
data Type t where
VNat :: Type Nat
VString :: Type String
VArrow :: Type a -> Type b -> Type (a -> b)
VPair :: Type a -> Type b -> Type (a, b)
VUnit :: Type ()
data SomeType = forall t. SomeType (Type t)
data SomeHom = forall a b. SomeHom (Type a) (Type b) (THom a b)
type Context = [(TVar, SomeType)]
getType :: Context -> SomeType
getType [] = SomeType VUnit
getType ((_, SomeType ttyp) :: gamma) =
case getType gamma of
SomeType ctxT -> SomeType (VPair ttyp
find :: TVar -> Context -> SomeHom
find tvar ((tvar’, ttyp) :: gamma)
| tvar == tvar’ =
case (ttyp, getType gamma) of
(SomeType t, SomeType ctxT) ->
SomeHom (VPair t ctxT) t Fst

Ad hoc polymorphic functions

I have some data that I'd like to print (some is Maybe and some is not), and I'm trying to create a generic showField function as follows:
showField :: (Show a) => a -> Text
showField x
| isJust x = Text.pack $ show $ fromJust x
| isNothing x = "None"
| otherwise = Text.pack $ show x
This is throwing a rigid type error:
• Couldn't match expected type ‘Maybe a0’ with actual type ‘a’
‘a’ is a rigid type variable bound by
the type signature for:
showField :: forall a. Show a => a -> Text
at /data/users/jkozyra/fbsource/fbcode/experimental/jkozyra/hs/holdout_cleanup/HoldoutReaper.hs:244:18
• In the first argument of ‘isNothing’, namely ‘x’
In the expression: isNothing x
In a stmt of a pattern guard for
an equation for ‘showField’:
isNothing x
I generally understand this error, but I don't understand if there's a way to achieve what I'd like to. I've also tried pattern matching rather than guards, but can't quite work that out either. Is there something that I could construct in this format that would work?
It looks like you're trying to construct an adhoc polymorphic function - a function whose definition varies according to its type.
A parametric polymorphic function does the same thing to all data types:
both :: a -> (a,a)
both a = (a,a)
In Haskell, adhoc polymorphism is implemented using type classes:
class ShowField a where
showField :: a -> Text
instance Show a => ShowField (Maybe a) where
showField Nothing = "None"
showField (Just a) = Text.pack $ show a
However there's no way to define an instance for "all other types other than Maybe a" with type classes, so you just have to define instances for the types you actually care about:
class ShowField Int where
showField = Text.pack . show
class ShowField Float where
showField = Text.pack . show
You can cut down on boilerplate by using -XDefaultSignatures:
class ShowField' a where
showField :: a -> Text
default showField :: Show a => a -> Text
showField = Text.pack . show
instance ShowField' Int where
instance ShowField' Float where
The error tells us:
‘a’ is a rigid type variable bound by
the type signature for:
showField :: forall a. Show a => a -> Text
Basically, this tells us that according to the type signature you provided, the type of the first parameter is forall a. Show a (the 'forall a.' bit is implied by the signature), which means that the first parameter can be any type that is an instance of Show. It is a rigid type variable because it is defined by an explicit type signature.
It also tells us:
Couldn't match expected type ‘Maybe a0’ with actual type ‘a’
By applying the functions isJust and isNothing — both of type Maybe a -> Bool — to the first parameter you are also claiming the type of the first parameter is Maybe a which is obviously not the same type as forall a. Show a => a -> Text.
You can turn this into a correct program simply by removing the type signature for showField, but that won't have the behavior you desire — the inferred type signature will be (Show a) => Maybe a -> Text which obviously only accepts values of Maybe a (where a is also an instance of Show).
In Haskell, you can't have a function that accepts values of both a and Maybe a¹. Without more context, it's unclear what your actual goal is, but there is almost certainly a more idiomatic way to achieve it.
¹ Unless you have a type class that has instances for both a and Maybe a.

How do I understand the set of valid inputs to a Haskell type constructor?

Warning: very beginner question.
I'm currently mired in the section on algebraic types in the Haskell book I'm reading, and I've come across the following example:
data Id a =
MkId a deriving (Eq, Show)
idInt :: Id Integer
idInt = MkId 10
idIdentity :: Id (a -> a)
idIdentity = MkId $ \x -> x
OK, hold on. I don't fully understand the idIdentity example. The explanation in the book is that:
This is a little odd. The type Id takes an argument and the data
constructor MkId takes an argument of the corresponding polymorphic
type. So, in order to have a value of type Id Integer, we need to
apply a -> Id a to an Integer value. This binds the a type variable to
Integer and applies away the (->) in the type constructor, giving us
Id Integer. We can also construct a MkId value that is an identity
function by binding the a to a polymorphic function in both the type
and the term level.
But wait. Why only fully polymorphic functions? My previous understanding was that a can be any type. But apparently constrained polymorphic type doesn't work: (Num a) => a -> a won't work here, and the GHC error suggests that only completely polymorphic types or "qualified types" (not sure what those are) are valid:
f :: (Num a) => a -> a
f = undefined
idConsPoly :: Id (Num a) => a -> a
idConsPoly = MkId undefined
Illegal polymorphic or qualified type: Num a => a -> a
Perhaps you intended to use ImpredicativeTypes
In the type signature for ‘idIdentity’:
idIdentity :: Id (Num a => a -> a)
EDIT: I'm a bonehead. I wrote the type signature below incorrectly, as pointed out by #chepner in his answer below. This also resolves my confusion in the next sentence below...
In retrospect, this behavior makes sense because I haven't defined a Num instance for Id. But then what explains me being able to apply a type like Integer in idInt :: Id Integer?
So in generality, I guess my question is: What specifically is the set of valid inputs to type constructors? Only fully polymorphic types? What are "qualified types" then? Etc...
You just have the type constructor in the wrong place. The following is fine:
idConsPoly :: Num a => Id (a -> a)
idConsPoly = MkId undefined
The type constructor Id here has kind * -> *, which means you can give it any value that has kind * (which includes all "ordinary" types) and returns a new value of kind *. In general, you are more concerned with arrow-kinded functions(?), of which type constructors are just one example.
TypeProd is a ternary type constructor whose first two arguments have kind * -> *:
-- Based on :*: from Control.Compose
newtype TypeProd f g a = Prod { unProd :: (f a, g a) }
Either Int is an expression whose value has kind * -> * but is not a type constructor, being the partial application of the type constructor Either to the nullary type constructor Int.
Also contributing to your confusion is that you've misinterpreted the error message from GHC. It means "Num a => a -> a is a polymorphic or qualified type, and therefore illegal (in this context)".
The last sentence that you quoted from the book is not very well worded, and maybe contributed to that misunderstanding. It's important to realize that in Id (a -> a) the argument a -> a is not a polymorphic type, but just an ordinary type that happens to mention a type variable. The thing which is polymorphic is idIdentity, which can have the type Id (a -> a) for any type a.
In standard Haskell polymorphism and qualification can only appear at the outermost level of the type in a type signature.
The type signature is almost correct
idConsPoly :: (Num a) => Id (a -> a)
Should be right, though i have no ghc on my phone to test this.
Also i think your question is quite broad, thus i deliberately answer only the concrete problem here.

Basic Haskell function types?

Super basic question - but I can't seem to get a clear answer. The below function won't compile:
randomfunc :: a -> a -> b
randomfunc e1 e2
| e1 > 2 && e2 > 2 = "Both greater"
| otherwise = "Not both greater"
main = do
let x = randomfunc 2 1
putStrLn $ show x
I'm confused as to why this won't work. Both parameters are type 'a' (Ints) and the return parameter is type 'b' (String)?
Error:
"Couldn't match expected type ‘b’ with actual type ‘[Char]’"
Not quite. Your function signature indicates: for all types a and b, randomfunc will return something of type b if given two things of type a.
However, randomFunc returns a String ([Char]). And since you compare e1 with 2 each other, you cannot use all a's, only those that can be used with >:
(>) :: Ord a => a -> a -> Bool
Note that e1 > 2 also needs a way to create such an an a from 2:
(> 2) :: (Num a, Ord a) => a -> Bool
So either use a specific type, or make sure that you handle all those constraints correctly:
randomfunc :: Int -> Int -> String
randomFunc :: (Ord a, Num a) => a -> a -> String
Both parameters are type 'a' (Ints) and the return parameter is type 'b' (String)?
In a Haskell type signature, when you write names that begin with a lowercase letter such as a, the compiler implicitly adds forall a. to the beginning of the type. So, this is what the compiler actually sees:
randomfunc :: forall a b. a -> a -> b
The type signature claims that your function will work for whatever ("for all") types a and b the caller throws at you. But this is not true for your function, since it only works on Int and String respectively.
You need to make your type more specific:
randomfunc :: Int -> Int -> String
On the other hand, perhaps you intended to ask the compiler to fill out a and b for you automatically, rather than to claim that it will work for all a and b. In that case, what you are really looking for is the PartialTypeSignatures feature:
{-# LANGUAGE PartialTypeSignatures #-}
randomfunc :: _a -> _a -> _b

Confusing about Haskell type inference

I have just started learning Haskell. As Haskell is static typed and has polymorphic type inference, the type of the identity function is
id :: a -> a
suggesting id can take any type as its parameter and return itself. It works fine when I try:
a = (id 1, id True)
I just suppose that at compile time, the first id is Num a :: a -> a, and the second id is Bool -> Bool. When I try the following code, it gives an error:
foo f a b = (f a, f b)
result = foo id 1 True
It shows the type of a must be the same type of b, since it works fine with
result = foo id 1 2
But is that true that the type of id's parameter can be polymorphic, so that a and b can be different type?
All right, this is a weird spooky corner of Haskell's type system. The problem here is that there are two ways to type inference your function foo.
-- rank 1
foo :: forall a b. (a -> b) -> a -> a -> (b, b)
foo f a b = (f a, f b)
-- rank 2
foo' :: (forall a. a -> a) -> a -> b -> (a, b)
foo' f a b = (f a, f b)
The second type is the one you want, but the first type is the one you're getting. The second type, as amalloy pointed out, is a rank-2 type (we're going to ignore what the two means but read the introduction in "Practical type inference for arbitrary-rank types" if you want a good explanation of ranks – don't be put off by the academic nature of the PDF file as the beginning is accessibly and clearly written).
We'll defer the definition of higher-ranked types for now and just say that the problem is that GHC is unable to infer the rank-2 type. Quote the paper:
Complete type inference is known to be undecidable for higher-rank (impredicative) type systems, but in practice programmers are more than willing to add type annotations to guide the type inference engine, and to document their code....
Kfoury and Wells show that typeability is decidable for rank ≤ 2, and undecidable for all ranks ≥ 3 (Kfoury & Wells, 1994). For the rank-2 fragment, the same paper gives a type inference algorithm. This inference algorithm is somewhat subtle, does not interact well with user-supplied type annotations, and has not, to our knowledge, been implemented in a production compiler.
Undecidable means there can be no algorithm that always leads to a correct yes-or-no decision. So there you have it: impossible to infer a rank-3-or-higher type, and it's too gosh-darn-hard to infer the rank-2 type.
Now, back to rank 2. The (forall a. a -> a) is what makes it rank-2. There's already an excellent Stack Overflow question about what the forall keyword means so I'll refer you to that, but basically it means you're able to call f a and f b in the expression (f a, f b) while having a and b be different types, which is what you wanted in the first place, before all this hot mess.
One last thing: The reason you don't normally see foralls in GHCi is that any foralls on the very outer scope are left off. So forall a b. (a -> b) -> a -> a -> (b, b) is equivalent to (a -> b) -> a -> a -> (b, b).
Overall this is a pain point of the language that's poorly explained.
(Hat tip to #amalloy in the comments.)

Resources