了解 newtype 以及何时使用它

Understading newtype and when to use it

我对纯脚本和函数式编程还很陌生

Newtypes are distinct from the point of view of the type system. This gives an extra layer of type safety.

这是我的数独求解器(到目前为止)的开头 ~50 行代码的样子:

newtype Index = Index Int
newtype Option = Option Int
newtype Column = Column Int
newtype Row = Row Int
newtype Box = Box Int
newtype Cell = Cell Int

derive newtype instance eqIndex :: Eq Index
derive newtype instance eqOption :: Eq Option
derive newtype instance eqColumn :: Eq Column
derive newtype instance eqRow :: Eq Row
derive newtype instance eqBox :: Eq Box
derive newtype instance eqCell :: Eq Cell

derive newtype instance semiringIndex :: Semiring Index
derive newtype instance semiringOption :: Semiring Option
derive newtype instance semiringColumn :: Semiring Column
derive newtype instance semiringRow :: Semiring Row
derive newtype instance semiringBox :: Semiring Box
derive newtype instance semiringCell :: Semiring Cell

derive newtype instance ringIndex :: Ring Index
derive newtype instance ringOption :: Ring Option
derive newtype instance ringColumn :: Ring Column
derive newtype instance ringRow :: Ring Row
derive newtype instance ringBox :: Ring Box
derive newtype instance ringCell :: Ring Cell

derive newtype instance commutativeRingIndex :: CommutativeRing Index
derive newtype instance commutativeRingOption :: CommutativeRing Option
derive newtype instance commutativeRingColumn :: CommutativeRing Column
derive newtype instance commutativeRingRow :: CommutativeRing Row
derive newtype instance commutativeRingBox :: CommutativeRing Box
derive newtype instance commutativeRingCell :: CommutativeRing Cell

derive newtype instance euclideanRingIndex :: EuclideanRing Index
derive newtype instance euclideanRingOption :: EuclideanRing Option
derive newtype instance euclideanRingColumn :: EuclideanRing Column
derive newtype instance euclideanRingRow :: EuclideanRing Row
derive newtype instance euclideanRingBox :: EuclideanRing Box
derive newtype instance euclideanRingCell :: EuclideanRing Cell

derive newtype instance showIndex :: Show Index
derive newtype instance showOption :: Show Option
derive newtype instance showColumn :: Show Column
derive newtype instance showRow :: Show Row
derive newtype instance showBox :: Show Box
derive newtype instance showCell :: Show Cell

class IsInt a where toInt :: a -> Int
instance ciInt :: IsInt Int where toInt a = a
instance ciIndex :: IsInt Index where toInt (Index a) = a
instance ciOption :: IsInt Option where toInt (Option a) = a
instance ciColumn :: IsInt Column where toInt (Column a) = a
instance ciRow :: IsInt Row where toInt (Row a) = a
instance ciBox :: IsInt Box where toInt (Box a) = a
instance ciCell :: IsInt Cell where toInt (Cell a) = a

可能,我很快就会需要 Ord 和其他一些人。

所以现在这阻止了我意外地使用 adding/subtracting 一个 index 和一个 row,或者将参数混合到操作这些类型的函数中。为了有效地为 Int 指定 7 个名称,这也是相当多的样板文件。

我猜这太过分了,我可能不应该打扰?我可以使用类型同义词来保持代码的描述性并失去一些类型安全性。

有another/better方法吗?

就像生活中的一切一样,没有秘诀。这是一个频谱。在一个极端上根本没有类型安全,或者在另一个极端上淹没在新类型中。一如既往,真相介于两者之间。

但要告诉您更具体的内容,我需要了解更大的设计。仅从这些定义来看是不可能的。可能您实际上确实需要所有这些,在这种情况下……嗯……您确实需要所有这些。

但根据我的经验,通常情况并非如此。一些需要思考的问题可能是:

  • 他们真的都需要 RingCommutativeRing 吗?不知怎的,我对此表示怀疑。
  • 是否真的需要将它们全部分开,还是可以将一些组合成一种?例如,RowColumn 可以是 Point
  • 如果您希望您的类型具有数字的所有特征,那么也许它实际上应该是一个数字?
  • 是否可以通过使用命名参数(通过记录)来实现类型安全?

我通常处理这些事情的方法是启动准系统,然后根据需要添加对内容的支持。您可能会惊讶于在实践中几乎不需要。

这一点首先适用于使用新类型。您真的有可能混淆行和列吗?或者你只是想堵住每一个可能的洞以防万一?如果是后者,那么是的,你最终会得到大量的孔塞,这并不奇怪。