具有多种参数类型的多态输入函数
出于纯粹的好奇,我想知道 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)
我想知道它是否可以实现,如果没有像 typeclasses 这样的助手,你是否可以做到。
回答
您正在寻找的是一个名为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
回答
*
不是允许使用任何类型的通配符。因此,你使用那个是错误的。
要键入您的函数,我们需要指定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
的 class 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)