学习 Purescript,一些有助于定义类型

Learning Purescript, some help defining types

我是函数式编程的新手。我用过一点 Ramda(JavaScript 库),但与 Purescript 中的类型系统完全不同。

我有一个想法,我觉得应该可以用 Purescript 的类型系统来表达,但我不确定从哪里开始。

假设我正在尝试为数独板定义一些类型

newtype Index = Index Int
newtype Column = Column Int
newtype Row = Row Int
newtype Box = Box Int

我想定义这些类型的加法形式

在sudocode中:

indexAddition :: (Index | Int) -> (Index | Int) -> Index
indexAddition a b = (a + b) % 81

RowAddition :: (Row | Int) -> (Row | Int) -> Row
RowAddition a b = (a + b) % 9

ColumnAddition 和 BoxAddition 可能会与 RowAddition 合并,因为它们基本相同。

-- I have to be able to say that a is a subset of Int, but Int isn't a type class
FooAddition ::  forall a. Int a => a -> a -> a
FooAddition a b = (a + b) % 9

不知何故,我觉得我可能一开始就走错了路。

有什么帮助吗?

为了直接回答您的问题,拥有适用于不同类型但有限的一组函数(也称为“重载函数”)的方法是类型 classes。更具体地说,这样的函数应该是 class 类型的方法,然后为您希望它使用的每种类型(或类型组合)创建一个实例。

所以最直接的方法是这样的:

class IndexAddition a b where
  indexAddition :: a -> b -> Index
instance addIntInt :: IndexAddition Int Int where
  indexAddition a b = Index ((a+b) % 81)
instance addIntIndex :: IndexAddition Int Index where
  indexAddition a (Index b) = Index ((a+b) % 81)
instance addIndexInt :: IndexAddition Index Int where
  indexAddition (Index a) b = Index ((a+b) % 81)
instance addIndexIndex :: IndexAddition Index Index where
  indexAddition (Index a) (Index b) = Index ((a+b) % 81)

如您所见,我制作了四个实例,每个实例对应 IndexInt 的每个组合。这可行,但不可否认有点复杂。特别是如果您添加第三个参数或第三种可能的类型。

为了使它更短一些并且更易于管理,您可能会发现,为了添加特定类型,您需要从它们那里获得一种转换为 Int 的方法。如果你有,你可以将两个参数都转换为 Int,然后添加,然后包裹在 Index:

class IsInt a where toInt :: a -> Int
instance ciIndex :: IsInt Index where toInt (Index a) = a
instance ciInt :: IsInt Int where toInt a = a

indexAddition :: forall a b. IsInt a => IsInt b => a -> b -> Index
indexAddition a b = Index ((toInt a + toInt b) % 81)

也就是说,我强烈建议您重新考虑您的设计。当然,以任意组合添加数字和索引的能力乍一看可能看起来整洁漂亮,但在实践中您可能永远不需要它。即使您在某些非常特殊的情况下这样做,也很容易 wrap/unwrap 根据需要设置值。相信我,我去过那里。很多次。