具有可能多态参数的函数的类型签名

18

我可以写一个函数的类型签名吗?如果可以,应该怎么写呢?

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)

1
正如@ErikR在他(现已删除的)答案中向您展示的那样,左侧是可行的,但rhs上的类型取决于f的类型,因此我猜您也需要使用类型族 - 如果这对您来说没问题,那么应该是可能的 - 当然这只是一个猜测 - 我所知道的是有一种表达方式 ;) - Random Dev
很高兴使用GHC扩展。 - Clinton
@user3237465:快速浏览(至少你的简化版本),我认为你会遇到麻烦,因为f2不再是forall a,而只是针对Int - Random Dev
@Carsten,类型族无法帮助您;“模式”或类型族的结果都无法量化。类实例也不能,所以我非常确定整个事情是不可能的。 - dfeuer
@dfeuer 是的,你说得对,抱歉 - 这是一个有趣的问题 - 我确实以为我可以轻松地用Idris解决它,但最终出现了“SCOPE ERROR”,我正在努力弄清楚这是否是一个错误还是我太蠢了:https://gist.github.com/CarstenKoenig/c6ed79f8c57eae2d1af55525ee089c88 - 有人能给我指点吗? - Random Dev
显示剩余2条评论
2个回答

15

不,你不能这样做。基本问题在于Haskell的语法让你误以为f1f2的类型比它们实际上更相似。一旦转换为GHC Core,它们看起来就非常不同:

f1 :: forall a . a -> [a]
f2 :: Int -> Int

不仅如此,相应的术语看起来非常不同:

f1 = Λa -> λ(x :: a) -> [x]
f2 = λ(x :: Int) -> 3 * x

正如您所看到的,f1f2实际上有着不同数量的参数,其中f1需要一个类型和一个,而f2只需要一个值。

在更普通的情况下,当您将f1放入期望类型为Int -> [Int]的函数上下文中时,GHC会为您应用f1所需的类型(即实例化f1为具体类型),一切都会很好。例如,如果您有:

g :: (Int -> [Int]) -> Bool

如果您对 f1 应用 g,GHC 实际上会将其编译为

g (f1 @Int)

但是在这里,您希望对实例化是否发生进行多态处理,而 GHC 不支持这种功能(我认为这将是对核心语言的一个相当激进和破坏性的改变)。

由于类实例、类型族模式和类型族结果不能被量化,我非常有信心说没有任何办法可以得到您想要的东西。


7
如果您不介意添加一个Proxy参数,使用类似于我在此处对类似问题的回答的变体,这实际上是可能的。
大部分来自那个答案的解释在这里也适用,但我们需要稍微扩展一下,通过添加另外两个辅助类型类(我在这里称之为ListBoth):
{-# 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)

ListBoth并不是最好的名称(特别是List),因此如果您使用它们,您可能需要想出更好的名称(尽管我不确定我会建议在生产代码中执行这种类型的技巧,除非您有一个非常好的理由)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接