Не уверен, почему этот образец охранника соответствует

Изучаю Haskell, и я не уверен, почему я не получаю ожидаемый результат, учитывая эти определения:

instance Ring Integer where
  addId  = 0
  addInv = negate
  mulId  = 1

  add = (+)
  mul = (*)

class Ring a where
  addId  :: a            -- additive identity
  addInv :: a -> a       -- additive inverse
  mulId  :: a            -- multiplicative identity

  add :: a -> a -> a     -- addition
  mul :: a -> a -> a     -- multiplication

Я написал эту функцию

squashMul :: (Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x y
  | (Lit mulId) <- x = y
  | (Lit mulId) <- y = x
squashMul x y = Mul x y

Тем не мение:

*HW05> squashMul (Lit 5) (Lit 1)
Lit 1

Если я напишу одну версию специально для Integer:

squashMulInt :: RingExpr Integer -> RingExpr Integer -> RingExpr Integer
squashMulInt x y
  | (Lit 1) <- x = y
  | (Lit 1) <- y = x
squashMulInt x y = Mul x y

Тогда я получаю ожидаемый результат.

Почему (Lit mulId) <- x совпадать, даже если х не (горит 1)?

1 ответ

Решение

Переменные, используемые при сопоставлении с образцом, считаются локальными переменными. Рассмотрим это определение для вычисления длины списка:

len (x:xs) = 1 + len xs
len _      = 0

переменные x а также xs являются локальными переменными для этого определения. В частности, если мы добавим определение для переменной верхнего уровня, как в

x = 10
len (x:xs) = 1 + len xs
len _      = 0

это не влияет на значение для len, Подробнее, первая картина (x:xs) не эквивалентно (10:xs), Если бы это было истолковано таким образом, мы бы сейчас len [5,6] == 0, нарушая предыдущий код! К счастью, семантика сопоставления с образцом устойчива к таким новым объявлениям, как x=10,

Ваш код

squashMul :: (Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x y
  | (Lit mulId) <- x = y
  | (Lit mulId) <- y = x
squashMul x y = Mul x y

на самом деле означает

squashMul :: (Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x y
  | (Lit w) <- x = y
  | (Lit w) <- y = x
squashMul x y = Mul x y

что неправильно, так как w может быть произвольным. То, что вы хотите, это, вероятно,

squashMul :: (Eq a, Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x y
  | (Lit w) <- x , w == mulId = y
  | (Lit w) <- y , w == mulId = x
squashMul x y = Mul x y

(The Eq a ограничение может зависеть от определения RingExpr, который не был размещен)

Вы также можете упростить все, чтобы:

squashMul :: (Eq a, Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x@(Lit w) y         | w == mulId = y
squashMul x         y@(Lit w) | w == mulId = x
squashMul x         y                      = Mul x y

или даже:

squashMul :: (Eq a, Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul (Lit w) y       | w == mulId = y
squashMul x       (Lit w) | w == mulId = x
squashMul x       y                    = Mul x y

Эта версия даже не использует паттерны, так как в этом нет необходимости.

Другие вопросы по тегам