scala 如何有条件地声明示例?

0pizxfdo  于 2023-03-08  发布在  Scala
关注(0)|答案(3)|浏览(169)

我怎么说B的一个示例可以为那些A的示例创建,而不为其他m创建呢?

-- 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

请注意,我 * 不 * 想说:

class A => class B -- B requires A

B不知道也不应该知道A。在class级别,BA无关。B仅根据其最小完整定义(在其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

这里说 allm s是B s,并且要求所有m也是A s,这不是真的,也不是我想说的,这不是Scala代码行(blow)所说的。
我想说的正是:有一个B,它定义了某个接口,还有一个完全不相关的AB不是根据A定义的,在它的 definition 站点也不需要A。现在,其他人沿着并且知道一种方法,如果m有一个A的示例,就可以为m创建一个B的示例。
在Scala中,我可以很容易地说:

implicit def fromA[M: A]: B[M] = ...

也就是说,* 如果 * 您可以证明MA(在Scala中,这是通过将implicitA[M]引入作用域来实现的),然后我可以自动为M构造B示例。特别是,这不会在所有M上强制B,并且如果A[M]在某些M中不成立,则不是错误。你可以自己为M构造B,上面的(第二个)Haskell代码并不等同于Scala代码。
我想在 haskell 找一个

disbfnqx

disbfnqx1#

你想做的事不能做 *;所有已经有A示例的东西也必须显式地作为B示例列出。2但是你可以最小化样板文件。
如果控制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

如果你不控制B的定义,你可以做一个 Package 器newtype并使用DerivingVia,如果你也不控制已经是A示例的类型的定义,可能会抛出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 Int )
deriving instance B Bool via (BViaA Bool)
deriving instance B Char via (BViaA Char)

另见:

yqlxgs2m

yqlxgs2m2#

instance A m => B m

不,这是你想要的。它说:“每当某个东西是A的示例时,使用这些规则使它成为B的示例。你可能还需要FlexibleContexts来解决约束问题。FlexibleContexts是许多Haskell扩展之一,它最终是无害的,每天都在许多项目中使用。

class A m => B m

这就是你所想的,它说:“为了成为B的示例,它必须已经是A的示例,当你第一次声明class时,你声明了这个约束,当你对一个示例施加约束时,它量化了这个示例。

ig9co6j1

ig9co6j13#

在Scala中,我可以很容易地说:

implicit def fromA[M: A]: B[M] = ...

我想在 haskell 找一个
scala implicit def fromA[M: A]: B[M] = ...表示,如果数据类型M是类型类A的示例,那么它就是类型类B的示例。
的直译

// Scala

trait A[F[_]]
trait B[F[_]]

implicit def fromA[M[_]: A]: B[M] = new B[M] {}

trait C[X]
implicit val ac: A[C] = new A[C] {}

trait D[X]
implicit val bd: B[D] = new B[D] {}

trait E[X]

implicitly[A[C]]
implicitly[B[C]]
//implicitly[A[D]] // doesn't compile
implicitly[B[D]]
//implicitly[A[E]] // doesn't compile
//implicitly[B[E]] // doesn't compile

https://scastie.scala-lang.org/DmytroMitin/Nz1BZQwEQfu6HFQpzBKNww/7

-- Haskell

{-# LANGUAGE UndecidableInstances, AllowAmbiguousTypes #-}

import Data.Kind (Type)
     
class A (m :: Type -> Type)
     
class B (m :: Type -> Type)
     
instance A m => B m
     
data C (a :: Type)
     
instance A C
     
data D (a :: Type)
     
instance {-# OVERLAPPING #-} B D
     
data E (a :: Type)
     
implicitly :: a => ()
implicitly = ()
     
test = implicitly @(A C)
test1 = implicitly @(B C)
--test2 = implicitly @(A D) -- doesn't compile
test3 = implicitly @(B D)
--test4 = implicitly @(A E) -- doesn't compile
--test5 = implicitly @(B E) -- doesn't compile

https://ideone.com/iZtAyh
这里我定义了三种数据类型CDECA的示例(因此自动是B的示例),DB的示例,但不是A的示例,E不是其中任何一种的示例。
所以,要么你想在Haskell中使用instance A m => B m,要么你不想在Scala中使用implicit def fromA[M[_]: A]: B[M] = ???
另一方面,这个

// Scala

trait A[F[_]]
trait A1[F[_]]
trait B[F[_]]

implicit def fromA[M[_]: A]: B[M] = new B[M] {}
implicit def fromA1[M[_]: A1]: B[M] = new B[M] {}

trait C[X]
implicit val ca: A[C] = new A[C] {}
trait D[X]
implicit val da1: A1[D] = new A1[D] {}

implicitly[B[C]] // compiles
implicitly[B[D]] // compiles

https://scastie.scala-lang.org/DmytroMitin/SpuuQYfqQZibQk6EiVQnHw
在Haskell中是不可能

-- Haskell

{-# LANGUAGE UndecidableInstances #-}

class A (m :: * -> *)

class A1 (m :: * -> *)

class B (m :: * -> *)

instance A m => B m

instance {-# OVERLAPPING #-} A1 m => B m

data C a

instance A C

data D a

instance A1 D

implicitly :: a => ()
implicitly = ()

test = implicitly @(B C)
test1 = implicitly @(B D)

https://ideone.com/mXXug0

error:
  Duplicate instance declarations:
    instance A m => B m
    instance [overlapping] A1 m => B m

使用OVERLAPPING/OVERLAPPABLE无法修复此问题。
How to implement Constraint reflection? [Ad-Hoc polymorphism based on available Constraints]
Take action based on a type parameter's typeclass?

相关问题