如何编写半群实例?
How to write a Semigroup Instance?
问题
给定数据类型,实现 Semigroup 实例。这是我要实现的数据类型:
data Or a b = Fst a | Snd b deriving (Eq, Show, Num)
。它应该像这样工作:
Prelude> Fst 1 <> Snd 2
Snd 2
Prelude> Fst 1 <> Fst 2
Fst 2
Prelude> Snd 1 <> Fst 2
Snd 1
Prelude> Snd 1 <> Snd 2
Snd 1
当我测试像 > Fst "help" <> Fst "me"
这样的值时它工作正常,但是当我测试其他值时我得到错误。当我尝试通过从错误中导出 classes 来修复这些错误时,我得到了更多错误。我在这里做错了什么?
我的代码
data Or a b =
Fst a
| Snd b
deriving (Eq, Show)
instance (Semigroup a, Semigroup b, Num a, Num b) => Semigroup (Or a b) where
(Snd a) <> _ = Snd a
_ <> (Snd a) = Snd a
(Fst a) <> (Fst b) = Fst b
错误
当我尝试用整数进行测试时 > Fst 1 <> Fst 2
我得到:
No instance for (Num a0) arising from a use of ‘it’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance RealFloat a => Num (Data.Complex.Complex a)
-- Defined in ‘Data.Complex’
instance Data.Fixed.HasResolution a => Num (Data.Fixed.Fixed a)
-- Defined in ‘Data.Fixed’
instance forall (k :: BOX) (f :: k -> *) (a :: k).
Num (f a) =>
Num (Data.Monoid.Alt f a)
-- Defined in ‘Data.Monoid’
...plus 21 others
In the first argument of ‘print’, namely ‘it’
In a stmt of an interactive GHCi command: print it
当我尝试导出 Num class data Or a b = Fst a | Snd b deriving (Eq, Show, Num)
我得到:
Can't make a derived instance of ‘Num (Or a b)’:
‘Num’ is not a derivable class
In the data declaration for ‘Or’
Failed, modules loaded: none.
在您的定义中,任何特定类型 class 的实例都不需要 a
或 b
,因为您实际上从未对包装值执行任何操作。
instance Semigroup (Or a b) where
(Snd a) <> _ = Snd a
_ <> (Snd a) = Snd a
(Fst a) <> (Fst b) = Fst b
由于您的 Or
类型与 Either
同构,请将其与 semigroups
包中的 Either
实例进行比较,后者同样对所涉及的类型没有任何限制.
instance Semigroup (Either a b) where
Left _ <> b = b
a <> _ = a
真正的问题是您对实例施加了不需要的约束。随便写
instance Semigroup (Or a b) where
(Snd a) <> _ = Snd a
_ <> (Snd a) = Snd a
(Fst a) <> (Fst b) = Fst b
正如 chepner 所展示的那样,您实际上可以减少行数和模式数,并且 return 参数之一作为结果,而不是构建它的新副本。这可能对提高效率有好处。
不太重要的是,您的模式中的括号都是多余的,因为 application 的优先级高于任何运算符。
问题
给定数据类型,实现 Semigroup 实例。这是我要实现的数据类型:
data Or a b = Fst a | Snd b deriving (Eq, Show, Num)
。它应该像这样工作:
Prelude> Fst 1 <> Snd 2
Snd 2
Prelude> Fst 1 <> Fst 2
Fst 2
Prelude> Snd 1 <> Fst 2
Snd 1
Prelude> Snd 1 <> Snd 2
Snd 1
当我测试像 > Fst "help" <> Fst "me"
这样的值时它工作正常,但是当我测试其他值时我得到错误。当我尝试通过从错误中导出 classes 来修复这些错误时,我得到了更多错误。我在这里做错了什么?
我的代码
data Or a b =
Fst a
| Snd b
deriving (Eq, Show)
instance (Semigroup a, Semigroup b, Num a, Num b) => Semigroup (Or a b) where
(Snd a) <> _ = Snd a
_ <> (Snd a) = Snd a
(Fst a) <> (Fst b) = Fst b
错误
当我尝试用整数进行测试时 > Fst 1 <> Fst 2
我得到:
No instance for (Num a0) arising from a use of ‘it’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance RealFloat a => Num (Data.Complex.Complex a)
-- Defined in ‘Data.Complex’
instance Data.Fixed.HasResolution a => Num (Data.Fixed.Fixed a)
-- Defined in ‘Data.Fixed’
instance forall (k :: BOX) (f :: k -> *) (a :: k).
Num (f a) =>
Num (Data.Monoid.Alt f a)
-- Defined in ‘Data.Monoid’
...plus 21 others
In the first argument of ‘print’, namely ‘it’
In a stmt of an interactive GHCi command: print it
当我尝试导出 Num class data Or a b = Fst a | Snd b deriving (Eq, Show, Num)
我得到:
Can't make a derived instance of ‘Num (Or a b)’:
‘Num’ is not a derivable class
In the data declaration for ‘Or’
Failed, modules loaded: none.
在您的定义中,任何特定类型 class 的实例都不需要 a
或 b
,因为您实际上从未对包装值执行任何操作。
instance Semigroup (Or a b) where
(Snd a) <> _ = Snd a
_ <> (Snd a) = Snd a
(Fst a) <> (Fst b) = Fst b
由于您的 Or
类型与 Either
同构,请将其与 semigroups
包中的 Either
实例进行比较,后者同样对所涉及的类型没有任何限制.
instance Semigroup (Either a b) where
Left _ <> b = b
a <> _ = a
真正的问题是您对实例施加了不需要的约束。随便写
instance Semigroup (Or a b) where
(Snd a) <> _ = Snd a
_ <> (Snd a) = Snd a
(Fst a) <> (Fst b) = Fst b
正如 chepner 所展示的那样,您实际上可以减少行数和模式数,并且 return 参数之一作为结果,而不是构建它的新副本。这可能对提高效率有好处。
不太重要的是,您的模式中的括号都是多余的,因为 application 的优先级高于任何运算符。