如何有条件地声明实例?

Ash*_*ary 4 haskell scala typeclass

我该如何说可以为那些属于 的实例但不提及其他s的实例B创建:mAm

-- A.hs
module A where 
class A m -- m :: * -> *

-- B.hs, totally unrelated 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
Run Code Online (Sandbox Code Playgroud)

请注意我不想

class A => class B -- B requires A
Run Code Online (Sandbox Code Playgroud)

他们B不知道也不应该知道A。与class级别B无关AB仅根据其最小完整定义进行定义,该定义在其where子句(此处未显示)之后表达,并且不了解A或任何其他类别。其实B是独立的。

我也不想说

instance A m => B m -- *all* m are B, this requires A m as well


-- actually this errors out with a "constraint is no smaller than head" 
-- which is fixed by `newtype`ing around the `m` in `B`
-- but this is besides the point
Run Code Online (Sandbox Code Playgroud)

这说所有 ms 都是Bs,并且还要求所有m也是As,这是不正确的,也不是我想说的。这不是 Scala 行(blow)所说的。

我想说的正是这样的:有一个B,它定义了某个接口。还有一个完全不相关的AB没有根据其定义,也没有在其定义A站点以任何方式要求。现在,其他人出现了,并且知道了一种方法来创建for的实例(如果存在for 的实例)。ABmA

在 Scala 中我可以轻松地说:

implicit def fromA[M: A]: B[M] = ...
Run Code Online (Sandbox Code Playgroud)

这就是说,如果您可以证明M是 an A(在 Scala 中,这是通过将 animplicit A[M]引入范围来实现的),那么我可以自动构造 的B实例M。特别是,这不会B对所有s 施加强制,并且如果对于某些 s 不成立M也不是错误。您可以自由地自行构建。上面的(第二个)Haskell 代码并不等同于这个 Scala 代码。A[M]MBM

我正在寻找 Haskell 中的等效项

更新显然在 Haskell 中没有办法做到这一点

更新2这是这个问题背后的具体案例无法以无标记最终方式/使用类型类和实例对我的组件进行编码

Dan*_*ner 7

你想要的却做不到* ; 所有已经拥有实例的事物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
Run Code Online (Sandbox Code Playgroud)

如果您不控制 的定义B,则可以创建一个包装器 newtype 并使用,如果您也不控制已经是 的实例的类型的定义,则DerivingVia可能会扔进去。StandaloneDerivingA

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 Int )
deriving instance B Bool via (BViaA Bool)
deriving instance B Char via (BViaA Char)
Run Code Online (Sandbox Code Playgroud)

也可以看看:

*好的。你想要的事情不可能理智地完成。我相信你可以使用一些扭曲IncoherentInstances,但它们很脆弱,我强烈建议不要使用它们。