Haskell:无法将预期类型“a -> a”与实际类型“Failable (a -> a)”相匹配
Haskell : Couldn't match expected type ‘a -> a’ with actual type ‘Failable (a -> a)’
我正尝试在 Haskell 中尝试仿函数和安全除法,但我遇到了这个小错误,而且我无法弄清楚原因。
这是我的代码:
module Main
where
data Failable a = Failure String | Success a
instance (Show a) => Show (Failable a) where
show (Failure s) = "Failure : " ++ s
show (Success x) = "Success : " ++ (show x)
instance Functor Failable where
fmap f (Success x) = Success (f x)
fmap _ (Failure s) = Failure s
(//) :: (Num a) => Failable a -> Failable a -> Failable a
_ // (Success 0) = Failure "Division by zero"
x // y = fmap (fmap (/) x) y
main = do
print $ (Success 1) // (Success 2)
并且输出:
main.hs:16:16:
Couldn't match expected type ‘a -> a’
with actual type ‘Failable (a -> a)’
Relevant bindings include
y :: Failable a (bound at main.hs:16:6)
x :: Failable a (bound at main.hs:16:1)
(//) :: Failable a -> Failable a -> Failable a
(bound at main.hs:15:1)
Possible cause: ‘fmap’ is applied to too many arguments
In the first argument of ‘fmap’, namely ‘(fmap (/) x)’
In the expression: fmap (fmap (/) x) y
我想出了这个定义:
(//) :: (Eq a, Fractional a) => Failable a -> Failable a -> Failable a
_ // (Success 0) = Failure "Division by zero"
f@(Failure _) // _ = f
_ // f@(Failure _) = f
(Success v) // y = fmap (/v) y
你的定义发生了什么是你在 (//)
的第二个等式中传递给 fmap
,Failable (a -> a)
类型的东西,它期望有 Failable (a -> a)
类型的东西=18=].
你可以做的是使它成为Applicative
的一个实例,并使你的Functor
成为可应用的,如下:
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
并同样创建一个仿函数的实例:
instance Applicative Failable where
pure = Success
(<*>) fun (Success x) = fmap (\f -> f x) fun
因此,您可以定义(//)
如下:
(//) :: (Eq a, Fractional a) => Failable a -> Failable a -> Failable a
_ // (Success 0) = Failure "Division by zero"
x // y = (<*>) (fmap (/) x) y
更新:
正如 Asad 在评论中指出的那样,写最后一行的更自然、更清晰的方式是:
x // y = (/) <$> x <*> y
我认为您正试图将 (/)
提升为 Failable
函子。
您可以使用模式匹配来实现,但我认为您希望单独使用 Functor
函数 (fmap
)。
然而这是不可能的。充其量,fmap (/)
可以提供
(/) :: Num a => a -> a -> a
fmap (/) :: Num a => Failable a -> Failable (a -> a)
而且我们没有通用的方法将丑陋的 Failable (a -> a)
变成 Failable a -> Failable a
。
这就是为什么我们有更强大的类型 class:Applicative
,正是为了做到这一点
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
所以...恭喜。您刚刚发现了引入这种类型 class 的问题。
我建议您编写一个 Applicative
实例。之后,(省略零检查)
(//) = liftA2 (/)
-- or, (this being the most popular style, nowadays)
x // y = (/) <$> x <*> y
-- or,
x // y = fmap (/) x <*> y
我正尝试在 Haskell 中尝试仿函数和安全除法,但我遇到了这个小错误,而且我无法弄清楚原因。
这是我的代码:
module Main
where
data Failable a = Failure String | Success a
instance (Show a) => Show (Failable a) where
show (Failure s) = "Failure : " ++ s
show (Success x) = "Success : " ++ (show x)
instance Functor Failable where
fmap f (Success x) = Success (f x)
fmap _ (Failure s) = Failure s
(//) :: (Num a) => Failable a -> Failable a -> Failable a
_ // (Success 0) = Failure "Division by zero"
x // y = fmap (fmap (/) x) y
main = do
print $ (Success 1) // (Success 2)
并且输出:
main.hs:16:16:
Couldn't match expected type ‘a -> a’
with actual type ‘Failable (a -> a)’
Relevant bindings include
y :: Failable a (bound at main.hs:16:6)
x :: Failable a (bound at main.hs:16:1)
(//) :: Failable a -> Failable a -> Failable a
(bound at main.hs:15:1)
Possible cause: ‘fmap’ is applied to too many arguments
In the first argument of ‘fmap’, namely ‘(fmap (/) x)’
In the expression: fmap (fmap (/) x) y
我想出了这个定义:
(//) :: (Eq a, Fractional a) => Failable a -> Failable a -> Failable a
_ // (Success 0) = Failure "Division by zero"
f@(Failure _) // _ = f
_ // f@(Failure _) = f
(Success v) // y = fmap (/v) y
你的定义发生了什么是你在 (//)
的第二个等式中传递给 fmap
,Failable (a -> a)
类型的东西,它期望有 Failable (a -> a)
类型的东西=18=].
你可以做的是使它成为Applicative
的一个实例,并使你的Functor
成为可应用的,如下:
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
并同样创建一个仿函数的实例:
instance Applicative Failable where
pure = Success
(<*>) fun (Success x) = fmap (\f -> f x) fun
因此,您可以定义(//)
如下:
(//) :: (Eq a, Fractional a) => Failable a -> Failable a -> Failable a
_ // (Success 0) = Failure "Division by zero"
x // y = (<*>) (fmap (/) x) y
更新:
正如 Asad 在评论中指出的那样,写最后一行的更自然、更清晰的方式是:
x // y = (/) <$> x <*> y
我认为您正试图将 (/)
提升为 Failable
函子。
您可以使用模式匹配来实现,但我认为您希望单独使用 Functor
函数 (fmap
)。
然而这是不可能的。充其量,fmap (/)
可以提供
(/) :: Num a => a -> a -> a
fmap (/) :: Num a => Failable a -> Failable (a -> a)
而且我们没有通用的方法将丑陋的 Failable (a -> a)
变成 Failable a -> Failable a
。
这就是为什么我们有更强大的类型 class:Applicative
,正是为了做到这一点
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
所以...恭喜。您刚刚发现了引入这种类型 class 的问题。
我建议您编写一个 Applicative
实例。之后,(省略零检查)
(//) = liftA2 (/)
-- or, (this being the most popular style, nowadays)
x // y = (/) <$> x <*> y
-- or,
x // y = fmap (/) x <*> y