Haskell 如何编写函数';两次';在哈斯克尔?

Haskell 如何编写函数';两次';在哈斯克尔?,haskell,types,Haskell,Types,我想写两次函数,它接受一个函数和一个参数,并应用函数两次。但是,它接收的函数应该在联合类型上工作 例如 输出 twice f a c f a b f b c f c error 例如 如何编写两种类型的f和两次传递的f。传递的东西通常被称为函数组合,可通过操作符使用 twice f = f . f f . g = \x -> f(g x) tweep是一个自合成(函数迭代)的示例,您可以通过f来表示。f 但请注意,Haskell中没有重载

我想写两次函数,它接受一个函数和一个参数,并应用函数两次。但是,它接收的函数应该在联合类型上工作

例如

输出

   twice f a
 c
   f a
   b
   f b 
   c
   f c
   error
例如

如何编写两种类型的f和两次传递的f。

传递的东西通常被称为函数组合,可通过
操作符使用

twice f = f . f
f . g = \x -> f(g x)
tweep
是一个自合成(函数迭代)的示例,您可以通过
f来表示。f


但请注意,Haskell中没有重载函数-一个作用域中的每个函数都只有一种类型和实现(尽管这种类型可能是多态的)。

这样的
两次
函数如下所示:

twice :: (a -> a) -> a -> a
twice f = f . f
假设您有一个名为
sayIt
的函数,它将
Int
值转换为英语

sayIt :: Int -> String
sayIt 1 = "One"
sayIt _ = "I don't know!"
无法使
函数两次运行
sayIt

*Main> sayIt (sayIt 1)

<interactive>:1:7:
    Couldn't match expected type `Int' against inferred type `String'
    In the first argument of `sayIt', namely `(sayIt 1)'
    In the expression: sayIt (sayIt 1)
    In the definition of `it': it = sayIt (sayIt 1)
*Main> twice sayIt 1

<interactive>:1:6:
    Couldn't match expected type `Int' against inferred type `String'
    In the first argument of `twice', namely `sayIt'
    In the expression: twice sayIt 1
    In the definition of `it': it = twice sayIt 1
例如:

*Main> twice sayIt2 (Left 1)
Right "One"
*Main> twice sayIt2 (Left 2)
Right "I don't know!"
*Main> twice sayIt2 (Right "Hello")
Right "Hello"

你在这里真的问了两件事:“我如何编写两次函数
”,以及“我如何编写两种不同类型的
f
?”

让我们考虑一下第一个问题。现在让Haskell推断类型,让我们考虑一下它应该是什么样子。它需要一个参数:
两次f=undefined
两次
然后返回一个函数,该函数接受一个参数并对其应用两次:
两次f=\x->f(fx)

但是这个函数的类型是什么?嗯,
x
必须是某种类型的
α
。由于我们计算
(fx)
,这意味着
f
必须是一个函数,它接受
α
并返回
β
f::α-/code>。但是,我们也计算
f(fx)
,因此
f
也必须将
β
作为输入,返回
γ
f::β->γ
。任何单个变量只能有一种类型,因此这告诉我们
α->β=β->γ
,以及
α=β
β=γ
。因此,
f::α->α
,因此
\x->f(fx)::α->α
;这意味着
两次::(α->α)->α->α

这回答了你的第一个问题。你会注意到我在上面说过,
f
必须是从一种类型到同一种类型的函数。这回答了您的第二个问题:不可能用两种不同的类型编写
f
。这是因为,正如我所说,任何单个变量可能只有一种(可能是多态的)类型。为什么?那么,除其他原因外,假设我们有一个变量
impossable
,它有两个类型签名,
impossable::Int
impossable::String
,还有两个绑定,
impossable=24
impossable=“absz”
。那么
显示不可能返回的是什么?
show
函数的类型为
show::showα=>α->字符串
;由于
Int
String
都是
Show
typeclass的实例,我们无法判断这将返回
“42”
还是
“absz\”
。这样的不一致性是我们只允许一种类型的原因

然而,所有的希望都没有失去!您还提到使用联合类型来实现
f
。在本文中,您可能指的是
other
类型(尽管Haskell中的所有数据类型都是一种称为歧视联合的联合类型)
或者
是一个接受两个类型参数的类型(就像列表类型接受一个类型参数一样);我们说它有种类[类型的类型]
或者:*->*->*
OR
是联合类型:
OR A B
A
的所有元素和
B
的所有元素组成,提升为
OR
。正如Michael Steele所说,您可以编写带有两个类型签名的函数,作为返回
other
值的函数:
f::otherδε->otherδε
。请注意,这是一个完全有效的值,可以作为参数传递给
两次,因为
δε
都是完全合法的类型。我们通过模式匹配在
上定义函数;
or
的两个构造函数是
Left::δ->orδε
Right::ε->orδε
,用于提升两种类型的值。那么,一个示例函数如下所示

f :: Either Int String -> Either Int String
f (Left  n) = Right $ "The number " ++ show n
f (Right s) = Left  $ length s

-- f (Left 3)               == Right "The number 3"
-- f (Right "The number 3") == Left 12
-- twice f (Left 3)         == Left 12
如果您真的想模仿您的示例并经历三种类型,从
α
β
γ
,您可以使用嵌套的
或者定义自己的数据类型。使用嵌套的
s,您可以

f :: Either Int (Either String Char) -> Either Int (Either String Char)
f (Left  n)         = Right $ Left  $ "The number " ++ show n
f (Right (Left  s)) = Right $ Right $ head $ drop 11 s
f (Right (Right c)) = Left  $ fromEnum c

-- f (Left 42)                      == Right (Left "The number 42")
-- f (Right (Left "The number 42")) == Right (Right '4')
-- f (Right (Right '4'))            == Left 52
-- twice f (Left 42)                == Right (Right '4')
使用新类型,您将获得:

data Either3 a b c = Left3 a | Mid3 b | Right3 c deriving (Eq, Ord, Read, Show)

f :: Either3 Int String Char -> Either3 Int String Char
f (Left3  n) = Mid3   $ "The number " ++ show n
f (Mid3   s) = Right3 $ head $ drop 11 s
f (Right3 c) = Left3  $ fromEnum c

-- f (Left3 42)             == Mid3 "The number 42"
-- f (Mid3 "The number 42") == Right3 '4'
-- f (Right3 '4')           == Left3 52
-- twice f (Left3 42)       == Right3 '4'
您还可以定义一个特定的
数据MyType=MyInt Int | MyStr String | MyChar Char
,并用
MyType
替换每个
字符串字符,用
MyInt
替换每个
Left3
,用
MyStr
替换每个
Mid3
,用
MyChar替换每个
;这实际上是同一件事,但不那么笼统


注意,多亏了Haskell的currying,我们可以将原始的
重写两次
两次fx=f(fx)
。事实上,更简单地说,如果我们导入
Control.Monad
,我们可以把它写成
tweef=f(.)f
,或者
tweef=join(.)
。这与回答这个问题无关,但由于其他原因(特别是
(>)α
例如
单子
,我不完全理解)这一点很有趣;如果您以前没有见过它,您可能想看一看。

在Haskell中不能重载函数(如果不是类型类的实例)。对,Haskell中不存在“联合类型”。你的朋友
f :: Either Int String -> Either Int String
f (Left  n) = Right $ "The number " ++ show n
f (Right s) = Left  $ length s

-- f (Left 3)               == Right "The number 3"
-- f (Right "The number 3") == Left 12
-- twice f (Left 3)         == Left 12
f :: Either Int (Either String Char) -> Either Int (Either String Char)
f (Left  n)         = Right $ Left  $ "The number " ++ show n
f (Right (Left  s)) = Right $ Right $ head $ drop 11 s
f (Right (Right c)) = Left  $ fromEnum c

-- f (Left 42)                      == Right (Left "The number 42")
-- f (Right (Left "The number 42")) == Right (Right '4')
-- f (Right (Right '4'))            == Left 52
-- twice f (Left 42)                == Right (Right '4')
data Either3 a b c = Left3 a | Mid3 b | Right3 c deriving (Eq, Ord, Read, Show)

f :: Either3 Int String Char -> Either3 Int String Char
f (Left3  n) = Mid3   $ "The number " ++ show n
f (Mid3   s) = Right3 $ head $ drop 11 s
f (Right3 c) = Left3  $ fromEnum c

-- f (Left3 42)             == Mid3 "The number 42"
-- f (Mid3 "The number 42") == Right3 '4'
-- f (Right3 '4')           == Left3 52
-- twice f (Left3 42)       == Right3 '4'