How are variable names chosen in type signatures inferred by GHC? - haskell

When I play with checking types of functions in Haskell with :t, for example like those in my previous question, I tend to get results such as:
Eq a => a -> [a] -> Bool
(Ord a, Num a, Ord a1, Num a1) => a -> a1 -> a
(Num t2, Num t1, Num t, Enum t2, Enum t1, Enum t) => [(t, t1, t2)]
It seems that this is not such a trivial question - how does the Haskell interpreter pick literals to symbolize typeclasses? When would it choose a rather than t? When would it choose a1 rather than b? Is it important from the programmer's point of view?

The names of the type variables aren't significant. The type:
Eq element => element -> [element] -> Bool
Is exactly the same as:
Eq a => a -> [a] -> Bool
Some names are simply easier to read/remember.
Now, how can an inferencer choose the best names for types?
Disclaimer: I'm absolutely not a GHC developer. However I'm working on a type-inferencer for Haskell in my bachelor thesis.
During inferencing the names chosen for the variables aren't probably that readable. In fact they are almost surely something along the lines of _N with N a number or aN with N a number.
This is due to the fact that you often have to "refresh" type variables in order to complete inferencing, so you need a fast way to create new names. And using numbered variables is pretty straightforward for this purpose.
The names displayed when inference is completed can be "pretty printed". The inferencer can rename the variables to use a, b, c and so on instead of _1, _2 etc.
The trick is that most operations have explicit type signatures. Some definitions require to quantify some type variables (class, data and instance for example).
All these names that the user explicitly provides can be used to display the type in a better way.
When inferencing you can somehow keep track of where the fresh type variables came from, in order to be able to rename them with something more sensible when displaying them to the user.
An other option is to refresh variables by adding a number to them. For example a fresh type of return could be Monad m0 => a0 -> m0 a0 (Here we know to use m and a simply because the class definition for Monad uses those names). When inferencing is finished you can get rid of the numbers and obtain the pretty names.
In general the inferencer will try to use names that were explicitly provided through signatures. If such a name was already used it might decide to add a number instead of using a different name (e.g. use b1 instead of c if b was already bound).
There are probably some other ad hoc rules. For example the fact that tuple elements have like t, t1, t2, t3 etc. is probably something done with a custom rule. In fact t doesn't appear in the signature for (,,) for example.

How does GHCi pick names for type variables? explains how many of these variable names come about. As Ganesh Sittampalam pointed out in a comment, something strange seems to be happening with arithmetic sequences. Both the Haskell 98 report and the Haskell 2010 report indicate that
[e1..] = enumFrom e1
GHCi, however, gives the following:
Prelude> :t [undefined..]
[undefined..] :: Enum t => [t]
Prelude> :t enumFrom undefined
enumFrom undefined :: Enum a => [a]
This makes it clear that the weird behavior has nothing to do with the Enum class itself, but rather comes in from some stage in translating the syntactic sequence to the enumFrom form. I wondered if maybe GHC wasn't really using that translation, but it really is:
{-# LANGUAGE NoMonomorphismRestriction #-}
module X (aoeu,htns) where
aoeu = [undefined..]
htns = enumFrom undefined
compiled using ghc -ddump-simpl enumlit.hs gives
X.htns :: forall a_aiD. GHC.Enum.Enum a_aiD => [a_aiD]
[GblId, Arity=1]
X.htns =
\ (# a_aiG) ($dEnum_aiH :: GHC.Enum.Enum a_aiG) ->
GHC.Enum.enumFrom # a_aiG $dEnum_aiH (GHC.Err.undefined # a_aiG)
X.aoeu :: forall t_aiS. GHC.Enum.Enum t_aiS => [t_aiS]
[GblId, Arity=1]
X.aoeu =
\ (# t_aiV) ($dEnum_aiW :: GHC.Enum.Enum t_aiV) ->
GHC.Enum.enumFrom # t_aiV $dEnum_aiW (GHC.Err.undefined # t_aiV)
so the only difference between these two representations is the assigned type variable name. I don't know enough about how GHC works to know where that t comes from, but at least I've narrowed it down!
Ørjan Johansen has noted in a comment that something similar seems to happen with function definitions and lambda abstractions.
Prelude> :t \x -> x
\x -> x :: t -> t
but
Prelude> :t map (\x->x) $ undefined
map (\x->x) $ undefined :: [b]
In the latter case, the type b comes from an explicit type signature given to map.

Are you familiar with the concepts of alpha equivalence and alpha substitution? This captures the notion that, for example, both of the following are completely equivalent and interconvertible (in certain circumstances) even though they differ:
\x -> (x, x)
\y -> (y, y)
The same concept can be extended to the level of types and type variables (see "System F" for further reading). Haskell in fact has a notion of "lambdas at the type level" for binding type variables, but it's hard to see because they're implicit by default. However, you can make them explicit by using the ExplicitForAll extension, and play around with explicitly binding your type variables:
ghci> :set -XExplicitForAll
ghci> let f x = x; f :: forall a. a -> a
In the second line, I use the forall keyword to introduce a new type variable, which is then used in a type.
In other words, it doesn't matter whether you choose a or t in your example, as long as the type expressions satisfy alpha-equivalence. Choosing type variable names so as to maximize human convenience is an entirely different topic, and probably far more complicated!

Related

Significance of scoped type variables standing for type variables and not types

In the GHC documentation for the ScopedTypeVariables extension, the overview states the following as a design principle:
A scoped type variable stands for a type variable, and not for a type. (This is a change from GHC’s earlier design.)
I know the general purpose of the scoped type variables extension, but I don't know the implications of the distinction made here between standing for type variables and standing for types. What is the significance of the difference, from the perspective of users of the language?
The comment above alludes to two designs which approached this decision differently and made different tradeoffs. What was the alternative design, and how does it compare to the one currently implemented?
tl;dr: The documentation says what it says because the old implementation of scoped typed variables in GHC was different, and the new documentation (over)emphasizes the contrast between the old behavior and the new behavior. In fact, the scoped typed variables you use with the ScopedTypeVariables extension in place are just plain old (rigid) type variables, and these are the same type variables you've been using in regular Haskell type signatures without scoping (even if you didn't realize they were "rigid"). It's true that scoped type variables don't simply "stand for types", but regular unscoped type variables don't simply "stand for types", either.
Longer answer:
First, setting aside the issue of scoped type variables, consider the following:
pluralize :: [a] -> [a]
pluralize x = x ++ "s"
If a, as a type variable, simply "stood for a type", this would be fine. GHC would determine that a stood for the type Char, and the resulting signature [Char] -> [Char] would be determined to be the correct type of pluralize, so there'd be no problem. In fact, if we were inferring the type of:
pluralize x = x ++ "s"
in a plain old Hindley-Milner (HM) type system, this is probably exactly what would happen. As an intermediate step while typing the (++) application, the type checker would assign x the type [a] for a "fresh" HM type variable a, and it would assign pluralize the type [a] -> [a] before unifying [a] with the type of "s" :: [Char] to unify a and Char.
Instead, this is rejected by the GHC type checker because a in this type signature is not an HM-style type variable and so does not simply stand for a type. Instead, it is a rigid (i.e., user-specified) Haskell type variable, and the type checker doesn't permit such a variable to unify with anything other than itself while defining pluralize.
Similarly, the following is rejected:
pairlist :: a -> b -> [a]
pairlist x y = [x,y]
even though, if a and b just stood for types, this would be fine (as it works for any a and b of kind * provided only that a and b are the same type). Instead, it is rejected by the type checker because two rigid Haskell type variables a and b can't unify.
Now, you might try to make the case that the issue is not that type variables are "rigid" and can't unify with concrete types (like Char) or with each other, but rather that there is an implicit quantification in Haskell type signatures, so that the signature for pluralize is actually:
pluralize :: forall a . [a] -> [a]
and so when a is determined to "stand for" Char, it is the contradiction with this forall a quantification that triggers the error. The problem with this argument is that both explanations are actually more or less equivalent. It is because Haskell type variables are rigid (i.e., because type signatures in Haskell are implicitly universally quantified) that the types cannot unify (i.e., that the unification contradicts the quantification). However, it turns out that the "rigid type variables" explanation is closer to what's actually happening in the GHC type checker than the "implicit quantification" explanation. That's why the error messages generated by the above definitions refer to inability to match rigid type variables rather than to contradictions with universal type variable quantifications.
Now, let's turn back to the question of scoped type variables. In the olden days, GHC's -fscoped-type-variables extension was implemented quite differently. In particular, for pattern type signatures, you were permitted to write things like the following (taken from the documentation for GHC 6.0):
f :: [Int] -> Int -> Int
f (xs::[a]) (y::a) = (head xs + y) :: a
and the documentation went on to say:
The pattern type signatures on the left hand side of f express the fact that xs must be a list of things of some type a; and that y must have this same type. The type signature on the expression (head xs) [sic] specifies that this expression must have the same type a. There is no requirement that the type named by "a" is in fact a type variable. Indeed, in this case, the type named by "a" is Int. (This is a slight liberalisation from the original rather complex rules, which specified that a pattern-bound type variable should be universally quantified.)
It went on to give some additional examples of use of scoped type variables, such as:
g (x::a) (y::b) = [x,y] -- a unifies with b
k (x::a) True = ... -- a unifies with Int
k (x::Int) False = ...
In 2006, Simon Peyton-Jones made a big commit (ac10f840) to add impredicativity to the type system which also ended up substantially changing the implementation of lexically scoped type variables. The commit text contains a detailed explanation of the change including the requirements for the new design.
A key design choice was that lexically scoped type variables now named rigid (i.e., user-specified polymorphic) Haskell type variables, rather than being more like the HM-style variables that simply stood for a type and were subject to unification.
That made the above examples (f, g, and k) illegal, because scoped type variables in pattern matches now behaved more like regular rigid type variables.
Soooo... the old design was probably a weird hack that made scoped type variables more like HM type variables and so quite different from "normal" Haskell type variables, and the new system brought them more into line with how unscoped type variables behaved.
However, complicating things further, #duplode's link in the comments references a proposal to partially "undo" this "restriction" in the context of signatures in pattern matches. I think it would be fair to say that the old design, which treated scoped type variables more like a special case, was inferior to the new design which better unified the treatment of scoped and unscoped type variables, and there is no desire to go back to the old implementation. However, the new, simpler implementation had the side effect of being unnecessarily restrictive for pattern signatures which perhaps ought to be treated as a special case where non-rigid type variables are permitted.
I'm adding this answer (to my own question) in order to expand on duplode's reference in the comments. ScopedTypeVariables is currently being changed to allow scoped type variables to stand for types instead of only type variables. The discussion for this change the motivation for the new and old designs. This does not, however, address the even earlier design mentioned in the question and in K. A. Buhr's answer.
In the current state, before the forthcoming change, the definition
prefix :: a -> [[a]] -> [[a]]
prefix (x :: b) yss = map xcons yss
where xcons ys = x : ys
is valid (with ScopedTypeVariables), in which b is a newly introduced type variable that stands for the same thing as a. On the other hand, if prefix is specialized to
prefix :: Int -> [[Int]] -> [[Int]]
prefix (x :: b) yss = map xcons yss
where xcons ys = x : ys
then the program is rejected: b is forbidden from standing for Int since Int is not a type variable. Simon Peyton Jones remarked on why it was designed so that b could not stand for Int:
At the time I was worried that it'd be confusing to have a type
variable that was just an alias for Int; that is not a type variable
at all. But in these days of GADTs and type equalities we are all used
to that. We'd make a different choice today.
In the current consensus of the GHC maintainers, the limitation against b standing for Int is viewed as unnatural, especially in light of the possibility of type equalities (a ~ Int) => .... The presence of such constraints blur the lines of what being "bound to a type variable" really means. Should the examples
f1 :: (a ~ Int) => Maybe a -> Int
f1 (Just (x :: b)) = ...
f2 :: (a ~ Int) => Maybe Int -> Int
f2 (Just (x :: a)) = ...
be permitted? Under the new proposal, all four examples above are allowed.
In my own view, the tension ultimately comes from the coexistence of two very different type annotation systems. One of them has the effect of preventing you from giving different names to the same type (for instance, you cannot write (\x -> x) :: a -> b or (\x -> x) :: Int -> b and expect b to be unified with a or Int). The other enables and encourages you to give new names to things (pattern type signatures like foo (x :: b) = ...), a feature which exists to enable you to name types that would otherwise be un-nameable. The leftover question is whether pattern type signatures should allow you to alias types that are already nameable. The answer at its core depends on which of the two precedents you find more compelling.
References:
Joachim Breitner "nomeata" (April-August 2018). Feature request ticket "ScopedTypeVariables could allow more programs", https://ghc.haskell.org/trac/ghc/ticket/15050
nomeata (April-August 2018). Pull request "Allow ScopedTypeVariables to refer to types", https://github.com/ghc-proposals/ghc-proposals/pull/128
Richard A. Eisenberg, Joachim Breitner, and Simon Peyton Jones (June 2018). "Type variables in patterns", https://www.microsoft.com/en-us/research/uploads/prod/2018/06/tyvars-in-pats-haskell18-preprint.pdf, especially sections 3.5 and 4.3
nomeata (August 2018). GHC proposal "Allow ScopedTypeVariables to refer to types", https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0029-scoped-type-variables-types.rst

Can I verify whether a given function type signature has a potential implementation?

In case of explicit type annotations Haskell checks whether the inferred type is at least as polymorphic as its signature, or in other words, whether the inferred type is a subtype of the explicit one. Hence, the following functions are ill-typed:
foo :: a -> b
foo x = x
bar :: (a -> b) -> a -> c
bar f x = f x
In my scenario, however, I only have a function signature and need to verify, whether it is "inhabited" by a potential implementation - hopefully, this explanation makes sense at all!
Due to the parametricity property I'd assume that for both foo and bar there don't exist an implementation and consequently, both should be rejected. But I don't know how to conclude this programmatically.
Goal is to sort out all or at least a subset of invalid type signatures like ones above. I am grateful for every hint.
Goal is to sort out all or at least a subset of invalid type signatures like ones above. I am grateful for every hint.
You might want to have a look at the Curry-Howard correspondence.
Basically, types in functional programs correspond to logical formulas.
Just replace -> with implication, (,) with conjunction (AND), and Either with disjunction (OR). Inhabited types are exactly those having a corresponding formula which is a tautology in intuitionistic logic.
There are algorithms which can decide provability in intuitionistic logic (e.g. exploiting cut-elimination in Gentzen's sequents), but the problem is PSPACE-complete, so in general we can't work with very large types. For medium-sized types, though, the cut-elimination algorithm works fine.
If you only want a subset of not-inhabited types, you can restrict to those having a corresponding formula which is NOT a tautology in classical logic. This is correct, since intuitionistic tautologies are also classical ones. Checking whether a formula P is not a classical tautology can be done by asking whether not P is a satisfiable formula. So, the problem is in NP. Not much, but better than PSPACE-complete.
For instance, both the above-mentioned types
a -> b
(a -> b) -> a -> c
are clearly NOT tautologies! Hence they are not inhabited.
Finally, note that in Haskell undefined :: T and let x = x in x :: T for any type T, so technically every type is inhabited. Once one restricts to terminating programs which are free from runtime errors, we get a more meaningful notion of "inhabited", which is the one addressed by the Curry-Howard correspondence.
Here's a recent implementation of that as a GHC plugin that unfortunately requires GHC HEAD currently.
It consists of a type class with a single method
class JustDoIt a where
justDoIt :: a
Such that justDoIt typechecks whenever the plugin can find an inhabitant of its inferred type.
foo :: (r -> Either e a) -> (a -> (r -> Either e b)) -> (r -> Either e (a,b))
foo = justDoIt
For more information, read Joachim Breitner's blogpost, which also mentions a few other options: djinn (already in other comments here), exference, curryhoward for Scala, hezarfen for Idris.

Relationship between Haskell's 'forall' and '=>'

I'm having trouble wrapping my mind around the relationship (and interactions) between Haskell's forall and => (and for that matter the . that often connects them).
For example
λ> :t (+)
λ> :t id
give
(+) :: forall a. Num a => a -> a -> a
id :: forall a. a -> a
and while I understand how these work in these specific cases, I'm not comfortable parsing the expressions (signatures?) forall a. Num a => or forall a. themselves into something meaningful, or that I can generally understand in more complex contexts.
What do forall a. Num a => and forall a. mean? Specifically, what is the roles played in each by forall, => and a?
(As another perspective, without invoking the "implicit dictionary passing" implementation of type classes):
forall a. in Haskell means "for every type a".1 It's introducing a type variable, and declaring that the rest of the type expression has to be valid whatever choice is made for a.
You usually don't see it in basic Haskell (without turning on any extensions in GHC), because it's not necessary; you just use type variables in your type signature, and GHC automatically assumes there are foralls introducing those variables at the start of the expression.
For example:
zip :: forall a. ( forall b. ( [a] -> [b] -> [(a, b)] ))
zip :: forall a. forall b. [a] -> [b] -> [(a, b)]
zip :: forall a b. [a] -> [b] -> [(a, b)]
zip :: [a] -> [b] -> [(a, b)]
The above are all the same; they just tell us that zip can be a way of zipping a list of a together with a list of b to make a list of (a, b) pairs, whatever choice we feel like making for a and b.
forall mainly comes into play with extensions, because then you can introduce type variables with scopes other than the default ones assumed by GHC if you don't explicitly write them.
Now, the constraints => type syntax can be read roughly as "these constraints imply this type", or "provided these constraints hold, you can use this type". It's used all the time, even in vanilla Haskell with no extensions, so it's important to understand what it means and how it works and not just copy and paste and hope.
The => arrow allows us to state a set of constraints on the variables in the rest of the type expression; it lets us put limitations on what choices can be made to introduce the type variable. You should read it first by ignoring everything left of the => arrow, and reading the the right part on its own. This gives you the "shape" of the type. The stuff to the left of the => arrow tells you what kind of types you can use the rest of the type with.
An example:
(+) :: Num a => a -> a -> a
This means that (+) is exactly the same kind of thing as anything with a simpler type like a -> a -> a, except the Num a => is telling us that we're not free to choose any type a. We can only choose a type for a when we know that it is a member of the Num type class (another slightly more precise way of saying "a is a member of Num is "the constraint Num a holds").
Note that GHC is still assuming that there's an implicit forall a to introduce the type variable a here, so it really looks like:
(+) :: forall a. Num a => a -> a -> a
In which case you can read this off moderately easily as an English sentence once you know what forall a. and Num a => means: "For every type a, provided Num a holds, plus has the type a -> a -> a".
1 If you're familiar with formal logic at all, it's just an ASCII-friendly way of writing ∀a, a "universally quantified variable".
As the forall matter appears to be settled, I'll attempt to explain the => a bit. The things to the left of the => are arguments, much like ones to the left of a ->. But you don't apply these arguments manually, and they can only have specific types.
f :: Num a => a -> a
is a function that takes two arguments:
A Num a dictionary.
An a.
When you apply f, you just provide the a. GHC has to provide the Num a. If it's applied to a specific concrete type like Int, GHC knows Num Int and can supply it at the call site. Otherwise, it checks that Num a is provided by some outer context and uses that one. The great thing about Haskell's typeclass system is that it ensures that any two Num a dictionaries, however they are found, will be identical. So it doesn't matter where the dictionary comes from—it is sure to be the right one.
Further discussion
A lot of these things we're talking about aren't exactly part of Haskell so much as they're part of the way GHC interprets Haskell by translation to GHC core, AKA System FC, an extension of the very well-studied System F, AKA the Girard-Reynolds calculus. System FC is an explicitly typed polymorphic lambda calculus with algebraic datatypes, etc., but no type inference, no instance resolution, etc. After GHC checks the types in your Haskell code, it translates that code to System FC by a thoroughly mechanical process. It can do this confidently because the type checker "decorates" the code with all the information the desugarer needs to plumb all the dictionaries around. If you have a Haskell function that looks like
foo :: forall a . Num a => a -> a -> a
foo x y= x + y
then that will translate to something that looks like
foo :: forall a . Num a -> a -> a -> a
foo = /\ (a :: *) -> \ (d :: Num a) -> \ (x :: a) -> \ (y :: a) -> (+) #a d x y
The /\ is a type lambda—it's just line a normal lambda except it takes a type variable. The # represents application of a type to a function that takes one. The + is really just a record selector. It chooses the right field from the dictionary it's passed.
I suppose it helps if we add the implied parentheses:
(+) :: ∀ a . ( Num a => (a -> (a -> a)) )
id :: ∀ a . ( a -> a )
The ∀ always goes together with a .. It's basically special syntax meaning “anything between ∀ and . are type variables that I want to introduce into the following scope”†
=> denotes what Idris calls an implicit function: Num a is a dictionary for the instance Num a, and such a dictionary is implicitly needed whenever you're adding numbers. But whether a is a type variable here that was previously introduced by some ∀, or a fixed type, doesn't really matter. You could also have
(+) :: Num Int => Int -> Int -> Int
That's just superfluous, because the compiler knows that Int is a Num instance and hence automatically (implicitly!) chooses the right dictionary.
Really, there's no particular relationship between ∀ and =>, they just happen to be used often together.
†Actually this is a type-level lambda. The type expression ∀ a . b behaves analogously to the value level expression \a -> b.

Are type synonyms with typeclass constraints possible?

Feel free to change the title, I'm just not experienced enough to know what's really going on.
So, I was writing a program loosely based on this, and wrote this (as it is in the original)
type Row a = [a]
type Matrix a = [Row a]
Nothing special there.
However, I found myself writing a couple of functions with a type like this:
Eq a => Row a -> ...
So I thought that perhaps I could write this constraint into the type synonym definition, because to my mind it shouldn't be that much more complicated, right? If the compiler can work with this in functions, it should work as a type synonym. There are no partial applications here or anything or some kind of trickery (to my eyes).
So I tried this:
type Row a = Eq a => [a]
This doesn't work, and the compiler suggested switching on RankNTypes. The option made it compile, but the functions still required that I leave the Eq a => in their type declarations. As an aside, if I tried also having a type synonym like type Matrix a = [Row a] like before, it results in an error.
So my question(s) are thus:
Is it possible to have a type synonym with a typeclass constraint in its definition?
If not, why?
Is the goal behind this question achievable in some other way?
Constraints on a type variable can not be part of any Haskell type signature.
This may seem a bit of a ridiculous statement: "what's (==) :: Eq a => a -> a -> a then?"
The answer is that a doesn't really exist, in much the same way there is not really an x in the definition f x = x * log x. You've sure enough used the x symbol in defining that function, but really it was just a local tool used in the lambda-abstraction. There is absolutely no way to access this symbol from the outside, indeed it's not required that the compiler even generates anything corresponding to x in the machine code – it might just get optimised away.
Indeed, any polymorphic signature can basically be read as a lambda expression accepting a type variable; various writing styles:
(==) :: forall a . Eq a => a -> a -> a
(==) :: ∀ a . Eq a => a -> a -> a
(==) :: Λa. {Eq a} -> a -> a -> a
This is called System F.
Note that there is not really a "constraint" in this signature, but an extra argument: the Eq-class dictionary.
Usually you want to avoid having constraints in your type synonyms unless it's really necessary. Take the Data.Set API from containers for example.
Many operations in Data.Set require the elements of the set to be instances of Ord because Set is implemented internally as a binary tree. member or insert both require Ord
member :: Ord a => a -> Set a -> Bool
insert :: Ord a => a -> Set a -> Set a
However the definition of Set doesn't mention Ord at all.
This is because some operations on Set dont require an Ord instance, like size or null.
size :: Set a -> Int
null :: Set a -> Bool
If the type class constraint was part of the definition of Set, these functions would have to include the constraint, even though it's not necessary.
So yes, it is possible to have constraints in type synonyms using RankNTypes, but it's generally ill-advised. It's better to write the constraint for the functions that need them instead.

Programmatic type annotations in Haskell

When metaprogramming, it may be useful (or necessary) to pass along to Haskell's type system information about types that's known to your program but not inferable in Hindley-Milner. Is there a library (or language extension, etc) that provides facilities for doing this—that is, programmatic type annotations—in Haskell?
Consider a situation where you're working with a heterogenous list (implemented using the Data.Dynamic library or existential quantification, say) and you want to filter the list down to a bog-standard, homogeneously typed Haskell list. You can write a function like
import Data.Dynamic
import Data.Typeable
dynListToList :: (Typeable a) => [Dynamic] -> [a]
dynListToList = (map fromJust) . (filter isJust) . (map fromDynamic)
and call it with a manual type annotation. For example,
foo :: [Int]
foo = dynListToList [ toDyn (1 :: Int)
, toDyn (2 :: Int)
, toDyn ("foo" :: String) ]
Here foo is the list [1, 2] :: [Int]; that works fine and you're back on solid ground where Haskell's type system can do its thing.
Now imagine you want to do much the same thing but (a) at the time you write the code you don't know what the type of the list produced by a call to dynListToList needs to be, yet (b) your program does contain the information necessary to figure this out, only (c) it's not in a form accessible to the type system.
For example, say you've randomly selected an item from your heterogenous list and you want to filter the list down by that type. Using the type-checking facilities supplied by Data.Typeable, your program has all the information it needs to do this, but as far as I can tell—this is the essence of the question—there's no way to pass it along to the type system. Here's some pseudo-Haskell that shows what I mean:
import Data.Dynamic
import Data.Typeable
randList :: (Typeable a) => [Dynamic] -> IO [a]
randList dl = do
tr <- randItem $ map dynTypeRep dl
return (dynListToList dl :: [<tr>]) -- This thing should have the type
-- represented by `tr`
(Assume randItem selects a random item from a list.)
Without a type annotation on the argument of return, the compiler will tell you that it has an "ambiguous type" and ask you to provide one. But you can't provide a manual type annotation because the type is not known at write-time (and can vary); the type is known at run-time, however—albeit in a form the type system can't use (here, the type needed is represented by the value tr, a TypeRep—see Data.Typeable for details).
The pseudo-code :: [<tr>] is the magic I want to happen. Is there any way to provide the type system with type information programatically; that is, with type information contained in a value in your program?
Basically I'm looking for a function with (pseudo-) type ??? -> TypeRep -> a that takes a value of a type unknown to Haskell's type system and a TypeRep and says, "Trust me, compiler, I know what I'm doing. This thing has the value represented by this TypeRep." (Note that this is not what unsafeCoerce does.)
Or is there something completely different that gets me the same place? For example, I can imagine a language extension that permits assignment to type variables, like a souped-up version of the extension enabling scoped type variables.
(If this is impossible or highly impractical,—e.g., it requires packing a complete GHCi-like interpreter into the executable—please try to explain why.)
No, you can't do this. The long and short of it is that you're trying to write a dependently-typed function, and Haskell isn't a dependently typed language; you can't lift your TypeRep value to a true type, and so there's no way to write down the type of your desired function. To explain this in a little more detail, I'm first going to show why the way you've phrased the type of randList doesn't really make sense. Then, I'm going to explain why you can't do what you want. Finally, I'll briefly mention a couple thoughts on what to actually do.
Existentials
Your type signature for randList can't mean what you want it to mean. Remembering that all type variables in Haskell are universally quantified, it reads
randList :: forall a. Typeable a => [Dynamic] -> IO [a]
Thus, I'm entitled to call it as, say, randList dyns :: IO [Int] anywhere I want; I must be able to provide a return value for all a, not simply for some a. Thinking of this as a game, it's one where the caller can pick a, not the function itself. What you want to say (this isn't valid Haskell syntax, although you can translate it into valid Haskell by using an existential data type1) is something more like
randList :: [Dynamic] -> (exists a. Typeable a => IO [a])
This promises that the elements of the list are of some type a, which is an instance of Typeable, but not necessarily any such type. But even with this, you'll have two problems. First, even if you could construct such a list, what could you do with it? And second, it turns out that you can't even construct it in the first place.
Since all that you know about the elements of the existential list is that they're instances of Typeable, what can you do with them? Looking at the documentation, we see that there are only two functions2 which take instances of Typeable:
typeOf :: Typeable a => a -> TypeRep, from the type class itself (indeed, the only method therein); and
cast :: (Typeable a, Typeable b) => a -> Maybe b (which is implemented with unsafeCoerce, and couldn't be written otherwise).
Thus, all that you know about the type of the elements in the list is that you can call typeOf and cast on them. Since we'll never be able to usefully do anything else with them, our existential might just as well be (again, not valid Haskell)
randList :: [Dynamic] -> IO [(TypeRep, forall b. Typeable b => Maybe b)]
This is what we get if we apply typeOf and cast to every element of our list, store the results, and throw away the now-useless existentially typed original value. Clearly, the TypeRep part of this list isn't useful. And the second half of the list isn't either. Since we're back to a universally-quantified type, the caller of randList is once again entitled to request that they get a Maybe Int, a Maybe Bool, or a Maybe b for any (typeable) b of their choosing. (In fact, they have slightly more power than before, since they can instantiate different elements of the list to different types.) But they can't figure out what type they're converting from unless they already know it—you've still lost the type information you were trying to keep.
And even setting aside the fact that they're not useful, you simply can't construct the desired existential type here. The error arises when you try to return the existentially-typed list (return $ dynListToList dl). At what specific type are you calling dynListToList? Recall that dynListToList :: forall a. Typeable a => [Dynamic] -> [a]; thus, randList is responsible for picking which a dynListToList is going to use. But it doesn't know which a to pick; again, that's the source of the question! So the type that you're trying to return is underspecified, and thus ambiguous.3
Dependent types
OK, so what would make this existential useful (and possible)? Well, we actually have slightly more information: not only do we know there's some a, we have its TypeRep. So maybe we can package that up:
randList :: [Dynamic] -> (exists a. Typeable a => IO (TypeRep,[a]))
This isn't quite good enough, though; the TypeRep and the [a] aren't linked at all. And that's exactly what you're trying to express: some way to link the TypeRep and the a.
Basically, your goal is to write something like
toType :: TypeRep -> *
Here, * is the kind of all types; if you haven't seen kinds before, they are to types what types are to values. * classifies types, * -> * classifies one-argument type constructors, etc. (For instance, Int :: *, Maybe :: * -> *, Either :: * -> * -> *, and Maybe Int :: *.)
With this, you could write (once again, this code isn't valid Haskell; in fact, it really bears only a passing resemblance to Haskell, as there's no way you could write it or anything like it within Haskell's type system):
randList :: [Dynamic] -> (exists (tr :: TypeRep).
Typeable (toType tr) => IO (tr, [toType tr]))
randList dl = do
tr <- randItem $ map dynTypeRep dl
return (tr, dynListToList dl :: [toType tr])
-- In fact, in an ideal world, the `:: [toType tr]` signature would be
-- inferable.
Now, you're promising the right thing: not that there exists some type which classifies the elements of the list, but that there exists some TypeRep such that its corresponding type classifies the elements of the list. If only you could do this, you would be set. But writing toType :: TypeRep -> * is completely impossible in Haskell: doing this requires a dependently-typed language, since toType tr is a type which depends on a value.
What does this mean? In Haskell, it's perfectly acceptable for values to depend on other values; this is what a function is. The value head "abc", for instance, depends on the value "abc". Similarly, we have type constructors, so it's acceptable for types to depend on other types; consider Maybe Int, and how it depends on Int. We can even have values which depend on types! Consider id :: a -> a. This is really a family of functions: id_Int :: Int -> Int, id_Bool :: Bool -> Bool, etc. Which one we have depends on the type of a. (So really, id = \(a :: *) (x :: a) -> x; although we can't write this in Haskell, there are languages where we can.)
Crucially, however, we can never have a type that depends on a value. We might want such a thing: imagine Vec 7 Int, the type of length-7 lists of integers. Here, Vec :: Nat -> * -> *: a type whose first argument must be a value of type Nat. But we can't write this sort of thing in Haskell.4 Languages which support this are called dependently-typed (and will let us write id as we did above); examples include Coq and Agda. (Such languages often double as proof assistants, and are generally used for research work as opposed to writing actual code. Dependent types are hard, and making them useful for everyday programming is an active area of research.)
Thus, in Haskell, we can check everything about our types first, throw away all that information, and then compile something that refers only to values. In fact, this is exactly what GHC does; since we can never check types at run-time in Haskell, GHC erases all the types at compile-time without changing the program's run-time behavior. This is why unsafeCoerce is easy to implement (operationally) and completely unsafe: at run-time, it's a no-op, but it lies to the type system. Consequently, something like toType is completely impossible to implement in the Haskell type system.
In fact, as you noticed, you can't even write down the desired type and use unsafeCoerce. For some problems, you can get away with this; we can write down the type for the function, but only implement it with by cheating. That's exactly how fromDynamic works. But as we saw above, there's not even a good type to give to this problem from within Haskell. The imaginary toType function allows you to give the program a type, but you can't even write down toType's type!
What now?
So, you can't do this. What should you do? My guess is that your overall architecture isn't ideal for Haskell, although I haven't seen it; Typeable and Dynamic don't actually show up that much in Haskell programs. (Perhaps you're "speaking Haskell with a Python accent", as they say.) If you only have a finite set of data types to deal with, you might be able to bundle things into a plain old algebraic data type instead:
data MyType = MTInt Int | MTBool Bool | MTString String
Then you can write isMTInt, and just use filter isMTInt, or filter (isSameMTAs randomMT).
Although I don't know what it is, there's probably a way you could unsafeCoerce your way through this problem. But frankly, that's not a good idea unless you really, really, really, really, really, really know what you're doing. And even then, it's probably not. If you need unsafeCoerce, you'll know, it won't just be a convenience thing.
I really agree with Daniel Wagner's comment: you're probably going to want to rethink your approach from scratch. Again, though, since I haven't seen your architecture, I can't say what that will mean. Maybe there's another Stack Overflow question in there, if you can distill out a concrete difficulty.
1 That looks like the following:
{-# LANGUAGE ExistentialQuantification #-}
data TypeableList = forall a. Typeable a => TypeableList [a]
randList :: [Dynamic] -> IO TypeableList
However, since none of this code compiles anyway, I think writing it out with exists is clearer.
2 Technically, there are some other functions which look relevant, such as toDyn :: Typeable a => a -> Dynamic and fromDyn :: Typeable a => Dynamic -> a -> a. However, Dynamic is more or less an existential wrapper around Typeables, relying on typeOf and TypeReps to know when to unsafeCoerce (GHC uses some implementation-specific types and unsafeCoerce, but you could do it this way, with the possible exception of dynApply/dynApp), so toDyn doesn't do anything new. And fromDyn doesn't really expect its argument of type a; it's just a wrapper around cast. These functions, and the other similar ones, don't provide any extra power that isn't available with just typeOf and cast. (For instance, going back to a Dynamic isn't very useful for your problem!)
3 To see the error in action, you can try to compile the following complete Haskell program:
{-# LANGUAGE ExistentialQuantification #-}
import Data.Dynamic
import Data.Typeable
import Data.Maybe
randItem :: [a] -> IO a
randItem = return . head -- Good enough for a short and non-compiling example
dynListToList :: Typeable a => [Dynamic] -> [a]
dynListToList = mapMaybe fromDynamic
data TypeableList = forall a. Typeable a => TypeableList [a]
randList :: [Dynamic] -> IO TypeableList
randList dl = do
tr <- randItem $ map dynTypeRep dl
return . TypeableList $ dynListToList dl -- Error! Ambiguous type variable.
Sure enough, if you try to compile this, you get the error:
SO12273982.hs:17:27:
Ambiguous type variable `a0' in the constraint:
(Typeable a0) arising from a use of `dynListToList'
Probable fix: add a type signature that fixes these type variable(s)
In the second argument of `($)', namely `dynListToList dl'
In a stmt of a 'do' block: return . TypeableList $ dynListToList dl
In the expression:
do { tr <- randItem $ map dynTypeRep dl;
return . TypeableList $ dynListToList dl }
But as is the entire point of the question, you can't "add a type signature that fixes these type variable(s)", because you don't know what type you want.
4 Mostly. GHC 7.4 has support for lifting types to kinds and for kind polymorphism; see section 7.8, "Kind polymorphism and promotion", in the GHC 7.4 user manual. This doesn't make Haskell dependently typed—something like TypeRep -> * example is still out5—but you will be able to write Vec by using very expressive types that look like values.
5 Technically, you could now write down something which looks like it has the desired type: type family ToType :: TypeRep -> *. However, this takes a type of the promoted kind TypeRep, and not a value of the type TypeRep; and besides, you still wouldn't be able to implement it. (At least I don't think so, and I can't see how you would—but I am not an expert in this.) But at this point, we're pretty far afield.
What you're observing is that the type TypeRep doesn't actually carry any type-level information along with it; only term-level information. This is a shame, but we can do better when we know all the type constructors we care about. For example, suppose we only care about Ints, lists, and function types.
{-# LANGUAGE GADTs, TypeOperators #-}
import Control.Monad
data a :=: b where Refl :: a :=: a
data Dynamic where Dynamic :: TypeRep a -> a -> Dynamic
data TypeRep a where
Int :: TypeRep Int
List :: TypeRep a -> TypeRep [a]
Arrow :: TypeRep a -> TypeRep b -> TypeRep (a -> b)
class Typeable a where typeOf :: TypeRep a
instance Typeable Int where typeOf = Int
instance Typeable a => Typeable [a] where typeOf = List typeOf
instance (Typeable a, Typeable b) => Typeable (a -> b) where
typeOf = Arrow typeOf typeOf
congArrow :: from :=: from' -> to :=: to' -> (from -> to) :=: (from' -> to')
congArrow Refl Refl = Refl
congList :: a :=: b -> [a] :=: [b]
congList Refl = Refl
eq :: TypeRep a -> TypeRep b -> Maybe (a :=: b)
eq Int Int = Just Refl
eq (Arrow from to) (Arrow from' to') = liftM2 congArrow (eq from from') (eq to to')
eq (List t) (List t') = liftM congList (eq t t')
eq _ _ = Nothing
eqTypeable :: (Typeable a, Typeable b) => Maybe (a :=: b)
eqTypeable = eq typeOf typeOf
toDynamic :: Typeable a => a -> Dynamic
toDynamic a = Dynamic typeOf a
-- look ma, no unsafeCoerce!
fromDynamic_ :: TypeRep a -> Dynamic -> Maybe a
fromDynamic_ rep (Dynamic rep' a) = case eq rep rep' of
Just Refl -> Just a
Nothing -> Nothing
fromDynamic :: Typeable a => Dynamic -> Maybe a
fromDynamic = fromDynamic_ typeOf
All of the above is pretty standard. For more on the design strategy, you'll want to read about GADTs and singleton types. Now, the function you want to write follows; the type is going to look a bit daft, but bear with me.
-- extract only the elements of the list whose type match the head
firstOnly :: [Dynamic] -> Dynamic
firstOnly [] = Dynamic (List Int) []
firstOnly (Dynamic rep v:xs) = Dynamic (List rep) (v:go xs) where
go [] = []
go (Dynamic rep' v:xs) = case eq rep rep' of
Just Refl -> v : go xs
Nothing -> go xs
Here we've picked a random element (I rolled a die, and it came up 1) and extracted only the elements that have a matching type from the list of dynamic values. Now, we could have done the same thing with regular boring old Dynamic from the standard libraries; however, what we couldn't have done is used the TypeRep in a meaningful way. I now demonstrate that we can do so: we'll pattern match on the TypeRep, and then use the enclosed value at the specific type the TypeRep tells us it is.
use :: Dynamic -> [Int]
use (Dynamic (List (Arrow Int Int)) fs) = zipWith ($) fs [1..]
use (Dynamic (List Int) vs) = vs
use (Dynamic Int v) = [v]
use (Dynamic (Arrow (List Int) (List (List Int))) f) = concat (f [0..5])
use _ = []
Note that on the right-hand sides of these equations, we are using the wrapped value at different, concrete types; the pattern match on the TypeRep is actually introducing type-level information.
You want a function that chooses a different type of values to return based on runtime data. Okay, great. But the whole purpose of a type is to tell you what operations can be performed on a value. When you don't know what type will be returned from a function, what do you do with the values it returns? What operations can you perform on them? There are two options:
You want to read the type, and perform some behaviour based on which type it is. In this case you can only cater for a finite list of types known in advance, essentially by testing "is it this type? then we do this operation...". This is easily possible in the current Dynamic framework: just return the Dynamic objects, using dynTypeRep to filter them, and leave the application of fromDynamic to whoever wants to consume your result. Moreover, it could well be possible without Dynamic, if you don't mind setting the finite list of types in your producer code, rather than your consumer code: just use an ADT with a constructor for each type, data Thing = Thing1 Int | Thing2 String | Thing3 (Thing,Thing). This latter option is by far the best if it is possible.
You want to perform some operation that works across a family of types, potentially some of which you don't know about yet, e.g. by using type class operations. This is trickier, and it's tricky conceptually too, because your program is not allowed to change behaviour based on whether or not some type class instance exists – it's an important property of the type class system that the introduction of a new instance can either make a program type check or stop it from type checking, but it can't change the behaviour of a program. Hence you can't throw an error if your input list contains inappropriate types, so I'm really not sure that there's anything you can do that doesn't essentially involve falling back to the first solution at some point.

Resources