繁体   English   中英

为具有可能多态参数的函数键入签名

[英]Type signature for function with possibly polymorphic arguments

我可以,如果是,我该如何编写函数的类型签名:

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)

不,你不能。 基本问题是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只采用一个值。

在更常见的情况下,当您将f2放入期望类型函数的上下文中时,比如Int -> [Int] ,GHC会将f2应用于您所需的类型(即,将f2实例化为特定类型),并且会好的。 例如,如果你有

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

并且你将g应用于f ,GHC实际上会将其编译为

g (f @Int)

但是在这里你需要关于该实例化是否发生的多态性,GHC不支持(我认为这将是对核心语言的一种相当激进和破坏性的改变)。

由于类实例,类型族模式和类型族结果无法量化,我相信没有办法得到你想要的东西。

这实际上是可行的,如果你不介意添加一个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 ),所以如果你使用它,你可能想要提出更好的名称(虽然我不确定我会建议在生产代码中做这种类型的欺骗,除非你有一个很好的理由)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM