简体   繁体   中英

Ability to get a polymorphic function from monadic action in Haskell

I would like to be able to write an IO-action, which would return a polymorphic function, which could be used for different values, but it seems I can't. Can anyone help me doing that please?

f :: a -> Bool
f _ = True

getF :: Int -> (a -> Bool)
getF _ = f

getFIO :: IO (a -> Bool)
getFIO = return f

main :: IO ()
main = do
    -- this works
    print ((f :: Int -> Bool) (10::Int))
    print ((f :: String -> Bool) "asd")

    -- this works
    let f' = getF 10
    print (f' (10::Int))
    print (f' "asd")

    -- this doesn't
    f'' <- getFIO
    print (f'' (10::Int))
    print (f'' "asd")

    -- but this does
    f''' <- getFIO
    print (f''' (10::Int))
    f'''' <- getFIO
    print (f'''' "asd")

    return ()

Haskell does not allow you to use a polymorphic argument to a type constructor other than -> . You need to use a newtype to make use of that special exemption.

{-# LANGUAGE RankNTypes #-}
newtype ToBool = ToBool {getToBool :: forall a . a -> Bool}

f :: ToBool
f = ToBool (const True)

getFIO :: IO ToBool
getFIO = return f

You'll need to apply getToBool each time you want to use f , but that's okay.

To complement the other answers, I'd note that

f :: IO (a -> Bool)

actually means

f :: forall a. IO (a -> Bool)

which describes the following contract between caller and callee:

  1. The caller chooses a type a
  2. Then, f does some IO
  3. Finally, f returns a value of type a -> Bool

Note that a is chosen before the IO, hence the last function is monomorphic.

Instead, we need the following type

f :: IO (forall a. a -> Bool)

which postpones the choice of a after the IO is done. The above type requires the ImpredicativeTypes extension (which does not really work at the moment in GHC), or using a newtype wrapper as done in the other answers.

You can make it "work" with RankNTypes , but then you need a newtype wrapper:

-- might as well be Bool
newtype Pointless = Pointless (forall a. a -> Bool)

getFIO_ :: IO Pointless
getFIO_ = return (Pointless f)

main = do
    Pointless f'' <- getFIO_
    print (f'' (10::Int))
    print (f'' "asd")

So you might be better off rearranging your program to not use that extension, by changing getFIO :: IO Blah , defining a applyBlah :: Blah -> a -> Bool , and then using applyBlah f'' where you previously had f'' .

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