如何有条件地声明一个实例?

问题描述 投票:0回答:3

我怎么说

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 中的等价物

scala haskell typeclass
3个回答
0
投票
instance A m => B m 

不,这就是你想要的。这表示“每当某物是

A
的实例时,使用这些规则使其成为
B
的实例。您可能还需要
FlexibleContexts
来解决约束问题。
FlexibleContexts
是最终的许多 Haskell 扩展之一无害,每天都在许多项目中使用。

class A m => B m

这就是你的想法。它说“为了使某物成为

B
的实例,它必须已经是
A
的实例。您在第一次声明
class
时声明该约束。当您对实例施加约束时,它量化了实例。


0
投票

在 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 中。


0
投票

你想做的做不到;所有已经有

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

另见:

© www.soinside.com 2019 - 2024. All rights reserved.