我在试着理解
(*) . (+) 在哈斯克尔。我知道合成算子只是数学函数的标准组合-所以
(f . g) = f (g x)但是:
(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a我很难理解这种签名。我本来希望能做这样的事情:
((*) . (+)) 1 2 :: Num a => a -> a
= (* (+ 1 2))(*)的意思是什么?(+)类型签名?我试着用这样的方法来玩它(只是与它的签名相匹配):
((*) . (+)) 1 (\x -> x + 1) 1但这不能进行编译。在编写这些内容时,我试图遍历逻辑步骤,但我并不完全理解它是如何达到这个结果的(以及结果是什么)。
发布于 2014-12-27 04:57:36
我理解你的感受。一开始,我发现函数构图也很难掌握。帮助我摸索这件事的是打字签名。考虑:
(*) :: Num x => x -> x -> x
(+) :: Num y => y -> y -> y
(.) :: (b -> c) -> (a -> b) -> a -> c现在,当您编写(*) . (+)时,它实际上与(.) (*) (+)相同(即(*)是(.)的第一个参数,(+)是(.)的第二个参数):
(.) :: (b -> c) -> (a -> b) -> a -> c
|______| |______|
| |
(*) (+)因此,(*)的类型签名(即Num x => x -> x -> x)与b -> c统一
(*) :: Num x => x -> x -> x -- remember that `x -> x -> x`
| |____| -- is implicitly `x -> (x -> x)`
| |
b -> c
(.) (*) :: (a -> b) -> a -> c
| |
| |‾‾‾‾|
Num x => x x -> x
(.) (*) :: Num x => (a -> x) -> a -> x -> x因此,(+)的类型签名(即Num y => y -> y -> y)与Num x => a -> x统一
(+) :: Num y => y -> y -> y -- remember that `y -> y -> y`
| |____| -- is implicitly `y -> (y -> y)`
| |
Num x => a -> x
(.) (*) (+) :: Num x => a -> x -> x
| | |
| |‾‾‾‾| |‾‾‾‾|
Num y => y y -> y y -> y
(.) (*) (+) :: (Num (y -> y), Num y) => y -> (y -> y) -> y -> y我希望这能澄清Num (y -> y)和Num y是从哪里来的。留给您的是一个非常奇怪的(Num (y -> y), Num y) => y -> (y -> y) -> y -> y类型的函数。
令人感到奇怪的是,它期望y和y -> y都是Num的实例。y应该是Num的一个实例,这是可以理解的,但是y -> y是如何实现的呢?让y -> y成为Num的一个实例似乎不合逻辑。那不可能是对的。
但是,当您查看功能组合的实际功能时,这是有意义的:
( f . g ) = \z -> f ( g z)
((*) . (+)) = \z -> (*) ((+) z)所以你有一个函数\z -> (*) ((+) z)。因此,z显然必须是Num的一个实例,因为(+)被应用于它。因此,\z -> (*) ((+) z)的类型是Num t => t -> ...,其中...是(*) ((+) z)的类型,我们稍后会发现这一点。
因此,((+) z)是Num t => t -> t类型的,因为它需要多一个数字。然而,在将其应用于另一个数字之前,(*)将被应用于它。
因此,(*)希望((+) z)是Num的一个实例,这就是为什么希望t -> t是Num的一个实例。因此,将...替换为(t -> t) -> t -> t,并添加约束Num (t -> t),从而生成(Num (t -> t), Num t) => t -> (t -> t) -> t -> t类型。
您真正想要将(*)和(+)结合起来的方法是使用(.:)
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
f .: g = \x y -> f (g x y)因此,(*) .: (+)与\x y -> (*) ((+) x y)是相同的。现在给出了(+)的两个论点,确保((+) x y)实际上只是Num t => t而不是Num t => t -> t。
因此,((*) .: (+)) 2 3 5是(*) ((+) 2 3) 5,它是(*) 5 5,它是25,我相信这就是你想要的。
请注意,f .: g也可以编写为(f .) . g,(.:)也可以定义为(.:) = (.) . (.)。你可以在这里读到更多关于它的内容:
希望这能有所帮助。
发布于 2014-12-27 04:38:46
(*)和(+)现在都有类型签名Num a => a -> a -> a,如果编写它们,就会得到一些古怪的东西。
(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a这是因为(*)和(+)需要两个‘参数’。
(+)用一个参数就可以得到一个函数。.操作符期望这个函数(您看到的a -> a )。
以下是(*) . (+)的含义
x f y
(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a(*) . (+)将x f y映射到((x +) * f) y,其中f是从a到a的函数,也是一个数字。(*)期望一个函数的原因是为了使类型匹配,同时它需要两个参数,但是该函数必须是一个数字,因为(*)只在数字上工作。
实际上,这个函数根本就没有意义。
发布于 2014-12-27 07:43:05
一些扩展部分首先:
{-# LANGUAGE FlexibleContexts, FlexibleInstances, TypeSynonymInstances #-}如其他答案所示,您的功能是
weird :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
weird x g = (x +) * g但是这个函数确实有非奇怪的语义。
有一个关于差异列表的概念。因此,有一个不同整数的概念。我看到它们只在依赖类型的设置中使用(例如这里,但这不是唯一的情况)。该定义的相关部分是
instance Enum DiffInt where
toEnum n = (n +)
fromEnum n = n 0
instance Num DiffInt where
n + m = n . m
n * m = foldr (+) id $ replicate (fromEnum n) m这在Haskell中没有多大意义,但对于依赖类型可能很有用。
现在我们可以写
test :: DiffInt
test = toEnum 3 * toEnum 4或
test :: DiffInt
test = weird 3 (toEnum 4)在这两种情况下,fromEnum test == 12。
编辑
可以避免使用TypeSynonymInstances扩展:
{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}
weird :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
weird x g = (x +) * g
instance (Enum a, Num a) => Enum (a -> a) where
toEnum n = (toEnum n +)
fromEnum n = fromEnum $ n (toEnum 0)
instance (Enum a, Num a) => Num (a -> a) where
n + m = n . m
n * m = foldr (+) id $ replicate (fromEnum n) m
type DiffInt = Int -> Int和以前一样,我们可以写
test' :: DiffInt
test' = weird 3 (toEnum 4)但现在我们也可以写
-- difference ints over difference ints
type DiffDiffInt = DiffInt -> DiffInt
test'' :: DiffDiffInt
test'' = weird (toEnum 3) (toEnum (toEnum 4))和
main = print $ fromEnum $ fromEnum test'打印12。
EDIT2更好的链接添加了。
https://stackoverflow.com/questions/27664213
复制相似问题