Dav*_*ams 10 haskell typeclass
我想创建两个类型类,A和B,其中A是的超类B。中定义的功能B足以实现中的功能A。然后,如果我有一个带有for fun :: (A thing) => ...实例的约束的函数,例如,我希望能够传递to 而不创建for 的重复实例。BIntIntfunAInt
例如,假设我有一个类型类,可以检查value是否为“ even”。然后,我有另一个类型类,可以检查某个值是否可以被某个数整除。第二种类型的类足够强大,可以在第一种类型中实现这些功能,并且仅需要“偶数检查”功能的任何功能都应能够接受具有“可除数”功能的参数。
这是我认为的样子:
class IsEven a where
isEven :: a -> Bool
class (IsEven a) => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
isEven :: a -> Bool
isEven a = divisibleBy a 2
printIsEven :: (IsEven a) => a -> IO ()
printIsEven a = putStrLn (show (IsEven.isEven a))
instance IsEven Int -- I need to do this or I cannot create a DivisibleBy instance
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
myint :: Int
myint = 2
main :: IO ()
main = printIsEven myint
Run Code Online (Sandbox Code Playgroud)
但是,在编译时会产生警告:
[2 of 2] Compiling Main ( Foo.hs, Foo.o )
Foo.hs:11:10: warning: [-Wmissing-methods]
• No explicit implementation for
‘IsEven.isEven’
• In the instance declaration for ‘IsEven Int’
|
11 | instance IsEven Int
| ^^^^^^^^^^
Linking Foo ...
Run Code Online (Sandbox Code Playgroud)
并且在运行时,程序失败:
Foo: Foo.hs:11:10-19: No instance nor default method for class operation isEven
Run Code Online (Sandbox Code Playgroud)
如何在不将逻辑复制到的情况下实现这种子类型化效果instance IsEven?
mel*_*ene 13
据我所知,标准Haskell最接近的是
instance IsEven Int where
isEven n = n `divisibleBy` 2
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
Run Code Online (Sandbox Code Playgroud)
您不必重复逻辑(实际上,您可以isEven根据实现divisibleBy),但是您仍然需要提供一个明确的定义。
您必须为要创建的每个类型重复此模式DivisibleBy。
使用DefaultSignatures语言扩展,您还可以执行以下操作:
{-# LANGUAGE DefaultSignatures #-}
class IsEven a where
isEven :: a -> Bool
default isEven :: (DivisibleBy a) => a -> Bool
isEven n = n `divisibleBy` 2
class (IsEven a) => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
instance IsEven Int
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
Run Code Online (Sandbox Code Playgroud)
这会将默认实现移到类本身。现在,您确实可以说不instance IsEven Int提供实例主体。缺点是现在IsEven必须了解DivisibleBy,并且您只能提供一个default实现。
您不能在新类中重新定义方法,而该方法会影响旧类中的方法。如果要使方法像这样工作,则父类必须引用子类。
你需要的DefaultSignatures扩展,使这项工作。打开它,然后将您的类更改为此:
class IsEven a where
isEven :: a -> Bool
default isEven :: DivisibleBy a => a -> Bool
isEven a = divisibleBy a 2
class IsEven a => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
Run Code Online (Sandbox Code Playgroud)
对于GHC 8.6及更高版本,这也可以通过DerivingVia以下方式实现:
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GeneralisedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
-- Class definitions:
class IsEven a where
isEven :: a -> Bool
-- Note that we don't need to have IsEven as a superclass.
class DivisibleBy a where
divisibleBy :: a -> Int -> Bool
-- Boilerplate that only needs to be written once:
-- Boilerplate DivisibleBy instance generated with GeneralisedNewtypeDeriving.
newtype WrappedDivisibleBy a = WrapDivisibleBy { unwrapDivisibleBy :: a }
deriving DivisibleBy
instance DivisibleBy a => IsEven (WrappedDivisibleBy a) where
isEven n = n `divisibleBy` 2
-- Instance example:
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
-- Boilerplate IsEven instance generated with DerivingVia
-- (and StandaloneDeriving, as we aren't defining Int here).
deriving via (WrappedDivisibleBy Int) instance IsEven Int
Run Code Online (Sandbox Code Playgroud)
DerivingVia并非始终是一个选项(对于类,如类Traversable,它有一个额外的类型构造函数,将类型包装在类型签名中,因此它与角色系统冲突);但是,当它起作用时,它非常整洁。
| 归档时间: |
|
| 查看次数: |
447 次 |
| 最近记录: |