在过去一周左右的时间里,我一直在为Scala开发一个类型化、索引化的数组特性。我希望将该特征作为类型类提供,并允许库用户以他们喜欢的方式实现它。下面是一个示例,使用列表的列表来实现2D数组类型类:
// crate a 2d Array typeclass, with additional parameters
trait IsA2dArray[A, T, Idx0, Idx1] {
def get(arr: A, x: Int, y: Int): T // get a single element of the array; its type will be T
}
// give this typeclass method syntax
implicit class IsA2dArrayOps[A, T, Idx0, Idx1](value: A) {
def get(x: Int, y: Int)(implicit isA2dArrayInstance: IsA2dArray[A, T, Idx0, Idx1]): T =
isA2dArrayInstance.get(value, x, y)
}
// The user then creates a simple case class that can act as a 2d array
case class Arr2d[T, Idx0, Idx1] (
values: List[List[T]],
idx0: List[Idx0],
idx1: List[Idx1],
)
// A couple of dummy index element types:
case class Date(i: Int) // an example index element
case class Item(a: Char) // also an example
// The user implements the IsA2dArray typeclass
implicit def arr2dIsA2dArray[T, Idx0, Idx1] = new IsA2dArray[Arr2d[T, Idx0, Idx1], T, Idx0, Idx1] {
def get(arr: Arr2d[T, Idx0, Idx1], x: Int, y: Int): T = arr.values(x)(y)
}
// create an example instance of the type
val arr2d = Arr2d[Double, Date, Item] (
List(List(1.0, 2.0), List(3.0, 4.0)),
List(Date(0), Date(1)),
List(Item('a'), Item('b')),
)
// check that it works
arr2d.get(0, 1)
这一切看起来都很好。我遇到的困难是,我希望将索引类型约束为一个已批准类型的列表(用户可以更改)。由于程序不是所有已批准类型的原始所有者,所以我想用一个typeclass来表示这些已批准类型,并让已批准类型实现它:
trait IsValidIndex[A] // a typeclass, indicating whether this is a valid index type
implicit val dateIsValidIndex: IsValidIndex[Date] = new IsValidIndex[Date] {}
implicit val itemIsValidIndex: IsValidIndex[Item] = new IsValidIndex[Item] {}
然后更改typeclass定义以施加一个约束,即idx0
和idx1
必须实现isValidIndex
类型类(这里是开始不起作用的地方):
trait IsA2dArray[A, T, Idx0: IsValidIndex, Idx1: IsValidIndex] {
def get(arr: A, x: Int, y: Int): T // get a single element of the array; its type will be T
}
如果有人能提出一个解决这个问题的办法,或者一个实现同一目标的方法的全面改变,我将非常感激。我知道Scala3允许traits具有隐式参数,因此允许我直接在typeclass泛型参数列表中使用idx0:isValidIndex
约束,这将非常棒。但是换到3就这样感觉就像是一个相当大的锤子去敲碎一个相对较小的坚果。
我想解决办法是
isa2darray
类型类实现为抽象类,这样我就可以直接在上面使用idx0:isValidIndex
语法(在上面的链接中好心建议)。这是我最初的想法,但是a)它对用户不太友好,因为它要求用户将他们正在使用的任何类型包装到另一个类中,然后再扩展这个抽象类。
使用抽象类而不是traits有什么好处?
https://www.geeksforgeeks.org/Difference-betweet-traits-and-abstract-classes-in-scala/
除非您有类型类的层次结构(如猫的functor
、applicative
、monad
...)。Trait或抽象类(一个类型类)不能扩展多个抽象类(类型类),而它可以扩展多个Trait(类型类)。但无论如何,类型类的继承是很棘手的
我定义了一个自定义GADT,其中类型构造函数对类型变量有一个类型类约束,如下所示: 我假设我不必提到约束,因为它是在GADT定义中声明的。我唯一能想到的部分原因是GADT在函数中的位置。我对此不太了解,但据我所知,在中处于正位置,在中处于负位置。 我什么时候必须明确地提到类型类约束,什么时候GHC自己根据GADTs类型构造函数上的约束来解决它?
我是Haskell的初学者,正在尝试学习类型类和类型。我有以下示例(它代表了我正在研究的代数中的一个实际问题),其中我定义了一个仅包装Num实例的类型,以及一个定义二进制操作的typeclass。 当将定义为此实例时,我意识到我希望能够使用类型“改变”函数。需要说明的是:我想提供一个函数,并返回一个新的类型,它是的一个实例。假设我想这样做5次、10次,唯一的区别是不同的函数。上面的代码我当然可以复
泛型的类型约束 swapTwoValues(_:_:)函数和Stack类型可以用于任意类型. 但是, 有时在用于泛型函数的类型和泛型类型上, 强制其遵循特定的类型约束很有用. 类型约束指出一个类型形式参数必须继承自特定类, 或者遵循一个特定的协议、组合协议. 例如, Swift的Dictionary类型在可以用于字典中键的类型上设置了一个限制. 如字典中描述的一样,字典键的类型必须是可哈希的. 也
为了澄清我的问题,让我用一种或多或少相同的方式重新措辞: 为什么Haskell中有超类/类继承的概念?导致这种设计选择的历史原因是什么?例如,为什么有一个没有类层次结构的基库,只有相互独立的类型库是如此糟糕? 此外,我在类继承中看到的一个可能不太好的事情是:我认为一个类实例会默默地选择一个对应的超类实例,这可能是为该类型实现的最自然的实例。让我们把单子看作函子的子类。也许可以有不止一种方法来定义某
实例声明中的非法类型签名:succ::odd->Odd(使用InstanceSigs允许这样做) 没有它也能工作,但我想知道为什么会产生这个错误,如何为这个函数正确指定类型签名?