gpt4 book ai didi

haskell - 在用户定义类型和现有类型之间定义已经存在(例如在 Prelude 中)运算符的正确方法是什么?

转载 作者:行者123 更新时间:2023-12-04 00:13:48 26 4
gpt4 key购买 nike

假设我有一个包装现有类型的自定义类型,

newtype T = T Int deriving Show

并假设我希望能够将 Ts 相加,并且将它们相加应该会导致将包装的值相加;我会通过

instance Num T where
(T t1) + (T t2) = T (t1 + t2)
-- all other Num's methods = undefined

我认为到目前为止我们还不错。请告诉我到目前为止是否存在重大问题。

现在让我们假设我希望能够将 TInt 相乘,并且结果应该是一个 T 其包装value 是前者乘以 int;我会选择这样的:

instance Num T where
(T t1) + (T t2) = T (t1 + t2)
(T t) * k = T (t * k)
-- all other Num's methods = undefined

这显然不起作用,因为 class Num 声明了 (*)::a -> a -> a,因此需要两个操作数(和结果)都是同一类型。

即使将 (*) 定义为自由函数也会带来类似的问题(即 (*) 已经存在于 Prelude 中)。

我该如何处理?

至于这个问题的原因,我可以用下面的方法来解决

  • 在我的程序中,我想将 (Int,Int) 用于笛卡尔平面中的二维向量,
  • 但我也将 (Int,Int) 用于另一个不相关的事情,
  • 因此,我必须消除两者之间的歧义,至少对其中一个使用 newtype 或者,如果出于其他几个原因使用 (Int,Int),那为什么不把它们都做成 newtypes 包装 (Int,Int)
  • 由于 newtype Vec2D = Vec2D (Int,Int) 表示普通的向量,因此能够做到 Vec2D (2,3) * 4 == Vec2D ( 8,12).

最佳答案

已经经常询问非常相似的示例,答案是这不是数字类型,因此不应该有 Num 实例。它实际上是一个 vector space类型,因此您应该改为定义

{-# LANGUAGE TypeFamilies #-}

import Data.AdditiveGroup
import Data.VectorSpace

newtype T = T Int deriving Show

instance AdditiveGroup T where
T t1 ^+^ T t2 = T $ t1 + t2
zeroV = T 0
negateV (T t) = T $ -t

instance VectorSpace T where
type Scalar T = Int
k *^ T t = T $ k * t

那么你的 T -> Int -> T 操作符是 ^* , 就是 flip (*^).

这也导致了在重载具有不同含义的标准运算符时应该做的更一般的事情:只需将其设为单独的定义。你甚至不需要给它一个不同的名字,这也可以使用 qualified 模块导入来消除歧义。

请不要不完整地实例化类,尤其是Num。当有人使用具有这些类型的泛型函数时,这只会导致 php-ish 混淆,它编译得很好,但是当调用代码需要 Num 语义但该类型实际上无法提供时,它会在运行时严重中断.

关于haskell - 在用户定义类型和现有类型之间定义已经存在(例如在 Prelude 中)运算符的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65641353/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com