简体   繁体   中英

List of polymorphic functions in haskell?

Consider the code below:

t1 :: [Int] -> (Int,String)
t1 xs = (sum xs,show $ length xs)

t2 :: [Int] -> (Int,String)
t2 xs = (length xs, (\x -> '?') <$> xs)

t3 :: [Int] -> (Char,String)
t3 (x:xs) = ('Y',"1+" ++ (show $ length xs))
t3  []     = ('N',"empty")

These three functions have a type that only varies partially -- they are entirely usable without needing to know the type of the first component of the tuple they produce. This means that I can operate on them without needing to refer to that type:

fnListToStrs vs fs = (\x -> snd $ x vs) <$> fs

Loading these definitions into GHCi, all three of the functions work independently as an argument to fnListToStrs , and indeed I can pass in a list containing both t1 and t2 because they have the same type:

*Imprec> fnListToStrs [1,2] [t1,t2]
["2","??"]
*Imprec> fnListToStrs [1,2] [t3]
["1+1"]

But I can't pass all 3 at the same time, even though the divergence of types is actually irrelevant to the calculation performed:

*Imprec> fnListToStrs [1,2] [t1,t2]
["2","??"]
*Imprec> fnListToStrs [1,2] [t3]
["1+1"]

I have the feeling that making this work has something to do with either existential or impredicative types, but neither extension has worked for me when using the type declaration I expect fnListToStrs to be able to take, namely:

fnListToStrs :: [Int] -> [forall a.[Int]->(a,String)] -> [String]

Is there some other way to make this work?

Any way to put these functions into a list will require "wrapping" each of them in some fashion. The simplest wrapping is just

wrap :: (a -> (b, c)) -> a -> c
wrap f = snd . f

There are, indeed, other ways to wrap these (notably with existential types), but you've not given any information to suggest that any of those would be even slightly better in your application than this simplest version.

Here's an example where something more sophisticated might make sense. Suppose you have

data Blob a b = Blob [a -> b] [a]

Now imagine you want to make a list of values of type Blob ab that all have the same b type, but may have different a types. Actually applying each function to each argument could lead to a prohibitively large list of potential results, so it would make sense to write

data WrapBlob b where
  WrapBlob :: Blob a b -> WrapBlob b

Now you can make the list and postpone the decision of which function(s) to apply to which argument(s) without paying a prohibitive price.

Existential is correct, not impredicative. And Haskell doesn't have existentials, except through an explicit wrapper...

{-# LANGUAGE GADTs #-}

data SomeFstRes x z where
  SFR :: (x -> (y,z)) -> SomeFstRes x z

> fmap (\(SFR f) -> snd $ f [1,2]) [SFR t1, SFR t2, SFR t3]
["2","??","1+1"]

but, this really is a bit useless. Since you can't possibly do anything with the first result anyway, it's more sensible to just throw it away immediately and put the remaining function in a simple monomorphic list:

> fmap ($[1,2]) [snd . t1, snd . t2, snd . t3]
["2","??","1+1"]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM