【发布时间】:2021-01-01 12:04:51
【问题描述】:
我正在尝试在 Haskell 中实现一种动态类型的编程语言,它支持三种数据类型,我们称它们为 A、B 和 C,仅出于说明目的,我将让 A = Integer、@987654325 @ 和C = (Integer, Integer) (但你可以忽略这些类型的语义,这不是我关心的)。
为了在算术表达式中互换使用任何类型的值,我实现了一个代数数据类型Value:
data Value = A A
| B B
| C C
因为我希望能够添加和乘以值,所以我实现了类型类OP:
class Op a where
add :: a -> a -> a
mul :: a -> a -> a
现在,我还希望我的类型可以隐式转换(当两个不同的类型出现在算术表达式中时),根据以下规则:
- 如果两种类型都是
A,则不进行转换 - 如果其中一种类型为
A,则另一种转换为A - 否则,两种类型都转换为
B
为了实现这一点,我实现了另一个类型类,ImplicitlyConvertible:
class ImplicitlyConvertible a where
toA :: a -> A
toB :: a -> B
一个完整的例子如下所示:
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
module Value where
type A = Integer
type B = [Integer]
type C = (Integer,Integer)
data Value = A A
| B B
| C C
class ImplicitlyConvertible a where
toA :: a -> A
toB :: a -> B
instance ImplicitlyConvertible A where
toA = id
toB = error "can't convert A to B"
instance ImplicitlyConvertible B where
toA = sum
toB = id
instance ImplicitlyConvertible C where
toA = sum
toB c = [fst c, snd c]
instance ImplicitlyConvertible Value where
toA v = case v of
A a -> toA a
B b -> toA b
C c -> toA c
toB v = case v of
A a -> toB a
B b -> toB b
C c -> toB c
class Op a where
add :: a -> a -> a
mul :: a -> a -> a
instance Op A where
add = (+)
mul = (*)
instance Op B where
add = zipWith (+)
mul = zipWith (*)
valueOp :: (Value -> Value -> Value) -> (Value -> Value -> Value)
valueOp op (A v) v' = op (A v) (A $ toA v')
valueOp op v (A v') = op (A $ toA v) (A v')
valueOp op v v' = op (B $ toB v) (B $ toB v')
instance Op Value where
add = valueOp add
mul = valueOp mul
我有三个问题:
-
toB实际上并没有为A实现这一事实似乎是不干净的。即使它永远不应该被调用,我也想完全避免实现它。 -
instance ImplicitlyConvertible Value只是一堆我想去掉的样板代码。 -
我不确定我对
instance Op Value的实现是否合理。
我是不是一开始就走错了路?我怎样才能更干净地实现所有这些?
【问题讨论】:
标签: haskell