我可以,如果是,我该如何编写函数的类型签名:
g f x y = (f x, f y)
如此:
f1 :: a -> [a]
f1 x = [x]
x1 :: Int
x1 = 42
c1 :: Char
c1 = 'c'
f2 :: Int -> Int
f2 x = 3 * x
x2 :: Int
x2 = 5
这样:
g f1 x1 c1 == ([42], ['c']) :: ([Int], [Char])
g f2 x1 x2 == (126, 15) :: (Int, Int)
答案 0 :(得分:15)
f1
和f2
的类型比它们实际上更相似。一旦翻译成GHC Core,它们就会变得更加不同:
f1 :: forall a . a -> [a]
f2 :: Int -> Int
不仅如此,相应的术语看起来也有所不同:
f1 = Λa -> λ(x :: a) -> [x]
f2 = λ(x :: Int) -> 3 * x
正如您所看到的,f1
和f2
实际上有不同数量的参数,其中f1
采用类型和一个值,f2
只取一个值。
在更常见的情况下,当您将f2
放入期望类型函数的上下文中时,例如Int -> [Int]
,GHC会将f2
应用于您所需的类型(即,将f2
实例化为特定类型),一切都会好的。例如,如果你有
g :: (Int -> [Int]) -> Bool
并且您将g
应用于f
,GHC实际上会将其编译为
g (f @Int)
但是在这里你需要关于该实例化是否发生的多态性,GHC不支持(我认为这将是对核心语言的一种相当激进和破坏性的改变)。
由于类实例,类型族模式和类型族结果无法量化,我相信没有办法得到你想要的东西。
答案 1 :(得分:7)
如果您不介意使用my answer to a similar question here上的变体添加Proxy
参数,这实际上是可行的。
我对此答案的大多数解释都在这里,但我们需要通过添加更多帮助程序类型(我在这里调用List
和Both
)来稍微扩展它:< / p>
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ConstraintKinds #-}
import Data.Proxy
f1 :: a -> [a]
f1 x = [x]
x1 :: Int
x1 = 42
c1 :: Char
c1 = 'c'
f2 :: Int -> Int
f2 x = 3 * x
x2 :: Int
x2 = 5
class b ~ [a] => List a b
instance List a [a]
class (a ~ b, b ~ c) => Both a b c
instance Both a a a
g :: (c a r1, c b r2) =>
Proxy c -> (forall x r. c x r => x -> r) -> a -> b -> (r1, r2)
g _ f x y = (f x, f y)
这允许我们做
ghci> g (Proxy :: Proxy List) f1 x1 c1
([42],"c")
ghci> g (Proxy :: Proxy (Both Int)) f2 x1 x2
(126,15)
List
和Both
不是最好的名字(特别是List
),所以如果你使用它,你可能想要得到更好的名字(虽然我不确定无论如何,我建议在生产代码中使用这种类型的技巧,除非你有真的正当理由。)