Reputation: 23135
If I've got a function:
f :: c u => t -> m u
and a list:
x :: [t]
How can I produce a list:
y :: [forall v. c v => m v]?
Why?
Well I'm trying to populate a product type from a list of "rawdata.
Now I have a function:
read :: (SomeMonadError m, CanRead a) => RawData -> m a
Which can parse any input data either with success or failure.
I've also got a function:
populateProductType :: (Generic a, Applicative f, (some other constraints)) => [forall b. c b => f b] -> f a
Which given a product type a
, of which all it's constituents are constrained by c
, will happily go through and populate a
, just by repeatedly applying the applicative, which nicely catches any parse errors.
This is all good except I don't seem to be able to produce the polymorphic list I need to pass to populateProductType
.
Upvotes: 1
Views: 93
Reputation: 152682
You can't; the type [forall a. ...]
isn't even allowed as a type, let alone inhabited. But you can make a small newtype wrapper and get most of the way there.
{-# Language RankNTypes #-}
{-# Language ConstraintKinds #-}
data T -- yours
data M a -- yours
class C a -- yours
f :: C u => T -> M u
f = undefined -- yours
newtype Forall c f = Forall (forall a. c a => f a)
f' :: T -> Forall C M
f' t = Forall (f t) -- N.B. f' = Forall . f doesn't work
mapf' :: [T] -> [Forall C M]
mapf' = map f'
Upvotes: 2
Reputation: 70257
f :: c u => t -> m u
I think you're misunderstanding what this says. This is not a subtyping relation. It does not say "Give me a t
and I'll produce m u
for some u
". It's universal quantification. It says "Give me a t
and pick a type u
, and I'll give you m u
, for any choice of u
you like, as long as its c
".
The function f
is not deciding what to return based on its input argument. It's being told what to return by you, the programmer. Or, more realistically, by Haskell's powerful type inference. For instance, if you have a data structure that takes a Double
, and you construct the structure with read someData
for that field, then Haskell is deciding, at compile time, to parse whatever it sees as a Double
, simply because anything else would fail to typecheck.
So if you want to apply f :: c u => t -> m u
to every element of a [t]
, you are responsible for determining at compile-time what u
is. If u
is going to be the same for all values in the list, then the built-in map
will do. If it's not, then you are no longer applying the same function to each element of the list (you're applying different instances of a polymorphic class of functions), so map
is no longer appropriate. In the latter case, you'll likely be better suited making a fixed-length data structure that contains what you want and working with that, rather than a list.
Upvotes: 1