出于纯粹的好奇,我想知道在Haskell中是否可能实现以下功能:函数foo
接受另一个函数作为参数,该函数在foo
的体中被调用不止一次,在此过程中改变了参数的类型。
下面的代码不能编译,因为fn
的参数类型一旦被调用就被固定了,但希望它能说明我所说的。
main = putStrLn (foo id)
foo :: (* -> *) -> [Char] -- maybe I'm also getting the whole *-thing wrong
foo fn =
let
val1 = fn "hey"
val2 = fn 42
in
show (val1, val2)
我想知道这是否可以实现,如果没有像类型类这样的帮助,你是否可以做到这一点。
您正在寻找的是一个名为RankNTypes
的扩展。有了它,您可以将函数的类型写成:
{-# LANGUAGE RankNTypes #-}
foo :: (forall a. a -> a) -> [Char]
在这种情况下,您可能提供的唯一函数是id
,但是您也可以使用类型类来允许更有趣的多态函数作为参数。考虑函数的这个版本:
bar:: (forall a. Show a => a -> String) -> String
bar fn =
let
val1 = fn "hey"
val2 = fn 42
in
val1 <> val2
*
不是允许使用任何类型的通配符。因此,你用that是错误的。
要键入函数,我们需要指定fn
的类型。它必须是一个多态函数,返回一些可以被Show
编辑的值。
一个可能的解决方案是:
{-# LANGUAGE ScopedTypeVariables, RankNTypes #-}
foo :: forall b. Show b => (forall a. a -> b) -> [Char]
foo fn = let
val1 = fn "hey"
val2 = fn 42
in show (val1, val2)
这要求fn
接受任何类型a
,并返回一个固定类型b
,它属于Show
类。
如前所述,这并不是非常有用,因为fn
无法使用它的参数,因为它是泛型a
。
也许更有用的变体是将a
属于某个类型类c
,这样至少fn
的参数可以根据c
使用。
foo :: forall b c. (Show b, c String, c Int)
=> (forall a. c a => a -> b) -> [Char]
foo fn = let
val1 = fn "hey"
val2 = fn (42 :: Int)
in show (val1, val2)