我怎么说
B
的实例可以为那些 m
s 是 A
的实例,但对其他 m
s 什么都不说:
-- A.hs
module A where
class A m -- m :: * -> *
-- B.hs, totally underrated to A
module B where
class B m
-- Utilities.hs
module Utilities where
given A m then instance B m -- like given in Scala 3
请注意我确实不是想说:
class A => class B -- B requires A
B
不知道也不应该知道A
。在class
级别B
与A
无关。 B
仅根据其最小完整定义来定义,该定义在其 where
子句(此处未显示)之后表示,并且不知道 A
或任何其他类。事实上B
是独立的。
我也做不是想说:
instance A m => B m -- *all* m are B, this requires A m as well
这说我们需要
A m
来制作B m
(对于所有m
)这是不正确的,也不是我想说的(实际上错误出“约束不小于头”,由newtype
修复在m
中围绕B
)
我想说的正是: 有一个
B
,它定义了某个接口。还有一个完全不相关的A
。 B
没有根据 A
定义,也不需要 A
在其 definition 站点以任何方式。
现在有人来了,并且知道一种方法来为 B
创建一个 m
的实例,如果它有一个 A
的实例的话。
在 Scala 中我可以轻松地说:
implicit def fromA[M: A]: B[M] = ...
我正在寻找 Haskell 中的等价物
instance A m => B m
不,这就是你想要的。这表示“每当某物是
A
的实例时,使用这些规则使其成为 B
的实例。您可能还需要 FlexibleContexts
来解决约束问题。FlexibleContexts
是最终的许多 Haskell 扩展之一无害,每天都在许多项目中使用。
class A m => B m
这就是你的想法。它说“为了使某物成为
B
的实例,它必须已经是A
的实例。您在第一次声明class
时声明该约束。当您对实例施加约束时,它量化了实例。
在 Scala 中我可以轻松地说:
implicit def fromA[M: A]: B[M] = ...
我正在寻找 Haskell 中的等价物
直译
// Scala
trait A[_[_]]
trait B[_[_]]
implicit def fromA[M[_]: A]: B[M] = ???
trait C[_]
implicit val ac: A[C] = ???
implicitly[B[C]] // compiles
是
-- Haskell
{-# LANGUAGE UndecidableInstances, AllowAmbiguousTypes #-}
import Data.Kind (Constraint, Type)
class A (m :: Type -> Type)
class B (m :: Type -> Type)
instance A m => B m
data C (a :: Type)
instance A C
implicitly :: forall (a :: Constraint). a => ()
implicitly = ()
test :: ()
test = implicitly @(B C) -- compiles
所以要么你想要
instance A m => B m
在 Haskell 中,要么你不想 implicit def fromA[M[_]: A]: B[M] = ???
在 Scala 中。
你想做的做不到;所有已经有
A
实例的东西也必须明确地列为 B
实例。但是你可以最小化样板文件。
如果控制
B
的定义,可以使用DefaultSignatures
:
class A m where foo :: m -> m -> m
instance A Int where foo = {- ... -}
instance A Bool where foo = {- ... -}
instance A Char where foo = {- ... -}
class B m where
bar :: m -> m -> m -> m
default bar :: A m => m -> m -> m -> m
bar m0 m1 m2 = foo (foo m0 m1) m2
-- N.B. no instance body needed
instance B Int
instance B Bool
instance B Char
如果你不这样做,你可以制作一个包装器 newtype 并使用
DerivingVia
,如果你也不控制作为实例的类型的定义,则可能会折腾 StandaloneDeriving
。
class A m where foo :: m -> m -> m
instance A Int where foo = {- ... -}
instance A Bool where foo = {- ... -}
instance A Char where foo = {- ... -}
class B m where
bar :: m -> m -> m -> m
newtype BViaA m = BViaA m
instance A m => B (BViaA m) where
bar (BViaA m0) (BViaA m1) (BViaA m2) = BViaA (foo (foo m0 m1) m2)
-- still no body
deriving instance B Int via BViaA
deriving instance B Bool via BViaA
deriving instance B Char via BViaA
另见: