1

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.

Clinton
  • 22,361
  • 15
  • 67
  • 163
  • 2
    Frame challenge: why not give `populateProductType` the type `(Generic a, Applicative f, (some other constraints)) => [RawData] -> f a`? – Daniel Wagner Feb 13 '23 at 02:23
  • @DanielWagner I ended up taking your advise somewhat and doing some version of this, producing a function like `[t] -> (forall b. c b => t -> f b) -> f a`. It ended up calling the more generic one but at least clients could avoid the more crazy type if they didn't need it. – Clinton Feb 14 '23 at 09:21

2 Answers2

2

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'
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • 3
    "the type `[forall a. ...]` [isn't even allowed as a type](https://stackoverflow.com/a/33093888/791604), let alone inhabited." It has been since GHC 9.2 introduced quick-look impredicativity. E.g., `y = [readMaybe "123", readMaybe "True", readMaybe "'x'"] :: [forall a. Read a => Maybe a]` – Joseph Sible-Reinstate Monica Feb 13 '23 at 04:56
1
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.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116