简体   繁体   English

Haskell 中的类型签名混淆

[英]Type signature comfusion in Haskell

So I have this function in my code所以我的代码中有这个 function

type Player = Char

choose :: Player -> a -> a -> a
choose p a b
  | p == 'X' = a
  | p == 'O' = b

At one point it is used like so在某一时刻,它是这样使用的

(choose p (+) (-)) 5 5

I can clearly see that it chooses between the (+) or (-) function and then applies it on 5 and 5. In fact I even tested it in GHCI for myself.我可以清楚地看到它在 (+) 或 (-) function 之间进行选择,然后将其应用于 5 和 5。事实上,我什至自己在 GHCI 中对其进行了测试。

However, here are the type signatures for the aforementioned functions但是,这里是上述函数的类型签名

(+) :: Num a => a -> a -> a

(-) :: Num a => a -> a -> a

I simply do not understand how a function of this type signature can be passed to choose.我根本不明白这种类型的签名如何通过 function 来选择。 Shouldn't choose have a type signature like this?不应该选择有这样的类型签名吗?

choose :: Num a => Player -> (a -> a -> a) -> (a -> a -> a) -> (a -> a -> a)

Could someone help me shine a light on this?有人可以帮我解释一下吗?

Those are not the same a .那些是不一样a

The a is signature for choose is not the same a as in signature for (+) and (-) . choosea是签名与(+)(-) a签名不同。 They happen to have the same name, but they're separate type variables, not related to each other at all.它们碰巧有相同的名称,但它们是单独的类型变量,彼此完全不相关。

So, to clear up the confusion, let's rename one of them:因此,为了消除混淆,让我们重命名其中一个:

choose :: Player -> x -> x -> x
(-) :: Num a => a -> a -> a
(+) :: Num a => a -> a -> a

Now, if we apply choose p (+) (-) , that checks out just fine if we set x ~ (a -> a -> a)现在,如果我们应用choose p (+) (-) ,如果我们设置x ~ (a -> a -> a)就可以了

You need to remember that type variables can be instantiated on each use, and can be instantiated to complicated types that themselves have multiple parts A function type like a -> a -> a is just as good a type to substitute for type variables as is Int , or [Char] , or (Double, Double) , etc.您需要记住,类型变量可以在每次使用时实例化,并且可以实例化为本身具有多个部分的复杂类型 A function 类型,如a -> a -> a与替代类型变量一样好Int[Char](Double, Double)等。

So if we instantiate the a type variable to Char we get this:因此,如果我们将a类型变量实例化为Char ,我们会得到:

-- here choose is used as Player -> Char -> Char -> Char
choose p 'X' 'O'

And if we instantiate the a type variable to a -> a -> a , then we get this:如果我们将a类型变量实例化为a -> a -> a ,那么我们会得到:

-- here choose is used as Num a => Player -> (a -> a -> a) -> (a -> a -> a) -> (a -> a -> a)
choose p (+) (-)

So anytime you see a function's type taking an argument or returning a result that is a simple type variable, like id:: a -> a , that does not mean you can't apply it to functions Much more complicated-looking things like (t -> Int -> [t]) -> (t -> Int -> [t]) are particular examples of the full generality of what you can use a simple type like a -> a for.因此,无论何时你看到一个函数的类型接受一个参数或返回一个简单类型变量的结果,比如id:: a -> a ,这并不意味着你不能将它应用到函数中 看起来更复杂的东西,比如(t -> Int -> [t]) -> (t -> Int -> [t])是您可以使用简单类型(如a -> a for)的完整通用性的特定示例

You need to learn to see function types as not particularly special;您需要学习查看 function 类型不是特别特别; they are just one of many kinds of types that exist, and anything that claims to work for all types must work for function types too.它们只是存在的多种类型中的一种,任何声称适用于所有类型的东西都必须适用于 function 类型。

Also remember that type variables in two different type signatures are in different scopes.还要记住,两个不同类型签名中的类型变量在不同的范围内。 The variable a in the original choose:: Player -> a -> a -> a is not the same variable a in (+):: Num a => a -> a -> a or even the one in choose:: Num a => Player -> (a -> a -> a) -> (a -> a -> a) -> (a -> a -> a) .原始choose:: Player -> a -> a -> a中的变量a(+):: Num a => a -> a -> a中的变量a甚至是choose:: Num a => Player -> (a -> a -> a) -> (a -> a -> a) -> (a -> a -> a)中的变量不同。 choose:: Num a => Player -> (a -> a -> a) -> (a -> a -> a) -> (a -> a -> a) If it helps, while you're working things out you can always rename variables across all of the type signatures you're considering (and then optionally rename the variables in the final one to match an external source like GHC).如果它有帮助,那么在您解决问题时,您始终可以重命名您正在考虑的所有类型签名中的变量(然后可以选择重命名最后一个变量中的变量以匹配 GHC 等外部源)。 For example I could avoid confusion by saying that I'm considering (+):: Num b => b -> b -> b , and then substituting that for a in Player -> a -> a -> a to get Num b => Player -> (b -> b -> b) -> (b -> b -> b) -> (b -> b -> b) .例如,我可以通过说我正在考虑(+):: Num b => b -> b -> b来避免混淆,然后将其替换a Player -> a -> a -> a中的 a 以获得Num b => Player -> (b -> b -> b) -> (b -> b -> b) -> (b -> b -> b)

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

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