如何为我自己的类型创建实例?
How to create the instance for my own type?
我是 Haskell 的新手,正在学习 Functor、Applicative 和 Monad。
我检查了来自黑客的 Either
类型的 Functor 实例是:
data Either a b = Left a | Right b
instance Functor (Either a) where
fmap _ (Left x) = Left x
fmap f (Right y) = Right (f y)
我被要求为名为 MyEither
:
的扩展 Either
执行 Functor、Applicative、Monad 实例
data MyEither a b = MyLeft a | MyRight b | Nothing
deriving (Eq, Show)
我的问题是:
1 如何在我的实例中扩展 Nothing
?
2派生是否一定要写在实例中?
谁能给我一些提示?
实现实例时无需编写 deriving
。 deriving
用于在定义新类型时让编译器为您派生实例。
对于Nothing
,模式匹配就可以了,return对于Nothing
的执行都是FAM
。您无能为力。
这是一个类似的例子,可能会对您有所帮助。
data Nope a = NopeDataJpg deriving (Eq, Show)
instance Functor Nope where
fmap _ _ = NopeDataJpg
instance Applicative Nope where
pure x = NopeDataJpg
_ <*> _ = NopeDataJpg
instance Monad Nope where
return = pure
_ >>= _ = NopeDataJpg
另外,这里是 GHC 中 Maybe
的 Functor
, Applicative
, and Monad
实现。他们处理 Nothing
的方式与您需要对数据类型执行的操作非常相似。
写下实例时,首先要明确写出类型:
data MyEither a b = MyLeft a | MyRight b | MyNothing
deriving (Eq, Show)
instance Functor (MyEither a) where
-- fmap :: Functor f => (b -> c) -> f b -> f c
-- fmap :: (b -> c) -> MyEither a b -> MyEither a c
(我将在这里使用 MyNothing
以防止与 Maybe
的 Nothing
混淆)。
现在我们准备根据数据类型定义枚举所有的可能性:
fmap bc (MyLeft aValue) = result where
-- bc :: b -> c
-- MyLeft aValue :: MyEither a b
-- result :: MyEither a c
.....
这里我们必须产生一个MyEither a c
类型的值作为结果。函数 bc
在这里没有用,因为我们只能访问类型 a
:
的值 aValue :: a
MyLeft aValue :: MyEither a b
--------------------------------
aValue :: a
因此我们这里有两种可能:
result = MyLeft aValue
将构造一个新值,这次的类型 MyEither a c
由类型签名确定;或
result = ...
.
填满点,在1和2之间做出选择,完成本子句的定义,fmap bc (MyLeft aValue) = ...
。 (但请参阅此 post 底部的重要注释)
下一种可能是
fmap bc (MyRight bValue) = ...
这次我们确实可以访问 b
类型的值,因此函数 bc
可以派上用场,因为
bValue :: b
bc :: b -> c
-------------------------
bc bValue :: c
和
cValue :: c
--------------------------------
myRight cValue :: MyEither a c
因此这个子句的自然定义是
fmap bc (MyRight bValue) = MyRight cValue
where
cValue = ....
当然,与第一个条款中相同的第二个选项在技术上也是可行的。(再次,请参阅此 post 底部的重要说明) 当然不可能的是在这里使用 MyLeft
构造一个 MyEither a c
值,因为我们在这里绝对无法访问任何 a
类型的值.... 除了 undefined
.
现在只剩下一种可能性了,根据类型签名(记住,fmap :: (b -> c) -> MyEither a b -> MyEither a c
),我们的 fmap
必须处理 MyEither a b
类型的值。该值是 ...
fmap bc MyNothing =
同样,我们必须生成一个 MyEither a c
类型的值作为结果。这次我们无法访问 b
和 a
类型的任何值。所以除了 return ...
别无选择
.......
(当然没有使用 undefined
)。请完成定义。
希望您现在能够根据需要完成其他定义。
重要更新:上面提到的那些选择实际上根本就没有选择。如果我们所关心的只是做出适合该类型的定义,那么这些可能性确实存在。但是,Functor 等实例也必须合法。例如函子定律是
fmap id === id
fmap (f . g) === fmap f . fmap g
并且在每种情况下,只有一种可能性不会违反法律。特别是,fmap id (MyLeft _) = MyNothing
违反了第一个 Functor 定律。
我是 Haskell 的新手,正在学习 Functor、Applicative 和 Monad。
我检查了来自黑客的 Either
类型的 Functor 实例是:
data Either a b = Left a | Right b
instance Functor (Either a) where
fmap _ (Left x) = Left x
fmap f (Right y) = Right (f y)
我被要求为名为 MyEither
:
Either
执行 Functor、Applicative、Monad 实例
data MyEither a b = MyLeft a | MyRight b | Nothing
deriving (Eq, Show)
我的问题是:
1 如何在我的实例中扩展 Nothing
?
2派生是否一定要写在实例中?
谁能给我一些提示?
实现实例时无需编写 deriving
。 deriving
用于在定义新类型时让编译器为您派生实例。
对于Nothing
,模式匹配就可以了,return对于Nothing
的执行都是FAM
。您无能为力。
这是一个类似的例子,可能会对您有所帮助。
data Nope a = NopeDataJpg deriving (Eq, Show)
instance Functor Nope where
fmap _ _ = NopeDataJpg
instance Applicative Nope where
pure x = NopeDataJpg
_ <*> _ = NopeDataJpg
instance Monad Nope where
return = pure
_ >>= _ = NopeDataJpg
另外,这里是 GHC 中 Maybe
的 Functor
, Applicative
, and Monad
实现。他们处理 Nothing
的方式与您需要对数据类型执行的操作非常相似。
写下实例时,首先要明确写出类型:
data MyEither a b = MyLeft a | MyRight b | MyNothing
deriving (Eq, Show)
instance Functor (MyEither a) where
-- fmap :: Functor f => (b -> c) -> f b -> f c
-- fmap :: (b -> c) -> MyEither a b -> MyEither a c
(我将在这里使用 MyNothing
以防止与 Maybe
的 Nothing
混淆)。
现在我们准备根据数据类型定义枚举所有的可能性:
fmap bc (MyLeft aValue) = result where
-- bc :: b -> c
-- MyLeft aValue :: MyEither a b
-- result :: MyEither a c
.....
这里我们必须产生一个MyEither a c
类型的值作为结果。函数 bc
在这里没有用,因为我们只能访问类型 a
:
aValue :: a
MyLeft aValue :: MyEither a b
--------------------------------
aValue :: a
因此我们这里有两种可能:
result = MyLeft aValue
将构造一个新值,这次的类型MyEither a c
由类型签名确定;或result = ...
.
填满点,在1和2之间做出选择,完成本子句的定义,fmap bc (MyLeft aValue) = ...
。 (但请参阅此 post 底部的重要注释)
下一种可能是
fmap bc (MyRight bValue) = ...
这次我们确实可以访问 b
类型的值,因此函数 bc
可以派上用场,因为
bValue :: b
bc :: b -> c
-------------------------
bc bValue :: c
和
cValue :: c
--------------------------------
myRight cValue :: MyEither a c
因此这个子句的自然定义是
fmap bc (MyRight bValue) = MyRight cValue
where
cValue = ....
当然,与第一个条款中相同的第二个选项在技术上也是可行的。(再次,请参阅此 post 底部的重要说明) 当然不可能的是在这里使用 MyLeft
构造一个 MyEither a c
值,因为我们在这里绝对无法访问任何 a
类型的值.... 除了 undefined
.
现在只剩下一种可能性了,根据类型签名(记住,fmap :: (b -> c) -> MyEither a b -> MyEither a c
),我们的 fmap
必须处理 MyEither a b
类型的值。该值是 ...
fmap bc MyNothing =
同样,我们必须生成一个 MyEither a c
类型的值作为结果。这次我们无法访问 b
和 a
类型的任何值。所以除了 return ...
.......
(当然没有使用 undefined
)。请完成定义。
希望您现在能够根据需要完成其他定义。
重要更新:上面提到的那些选择实际上根本就没有选择。如果我们所关心的只是做出适合该类型的定义,那么这些可能性确实存在。但是,Functor 等实例也必须合法。例如函子定律是
fmap id === id
fmap (f . g) === fmap f . fmap g
并且在每种情况下,只有一种可能性不会违反法律。特别是,fmap id (MyLeft _) = MyNothing
违反了第一个 Functor 定律。