Оценка, пусть и где в Haskell

в настоящее время я изучаю Haskell и пытаюсь понять, как оцениваются typeclasses и как let и where работа. Этот код работает нормально:

{-# LANGUAGE FlexibleInstances #-}
class Expr a where
    literal :: Integer -> a

instance Expr Integer where
    literal = id

instance Expr [Integer] where
    literal i = [i]

coerceInteger :: Integer -> Integer
coerceInteger = id

main = print $ coerceInteger (literal 100) : literal 100 -- Prints [100,100]

но изменение основной функции на

main = print $ coerceInteger expr : expr
    where expr = literal 200

вызывает ошибку компилятора:

Couldn't match expected type `[Integer]' with actual type `Integer'
In the second argument of `(:)', namely `expr'
In the second argument of `($)', namely `coerceInteger expr : expr'
In the expression: print $ coerceInteger expr : expr

Я предполагаю, что это потому, что в первом main метод literal 100 оценивается дважды, тогда как во втором примере literal 200 оценивается только один раз, поэтому компилятор вынужден выбрать тип.

как я могу разложить этот код, чтобы избежать повторения, не вызывая эту ошибку? Я пробовал использовать let expr = literal 300 in ... но столкнулся с той же проблемой.

1 ответов


проблема в том, что literal 200 интерпретируется по-разному в двух разных контекстах с вашего первого примера. Думайте об этом как

((:) :: a -> [a] -> [a])
    ((coerceInteger :: Integer -> Integer) (literal 100 :: Expr a => a))
    (literal 100 :: Expr a => a)

только на основе типов компилятор определяет, что первый literal 100 должно иметь тип Integer, потому что он передается coerceInteger, так как он должен принимать значение типа Integer. Это также устанавливает тип (:) теперь Integer -> [Integer] -> [Integer], подразумевая, что последний literal 100 должно иметь тип [Integer].

в второй пример: вы говорите, что оба они имеют одинаковое значение и, следовательно, один и тот же тип, что невозможно, потому что второй должен быть списком для (:) чтобы проверить тип.

это на самом деле происходит из-за страшного ограничения Мономорфизма. Вы можете решить эту проблему двумя способами: Во-первых, отключить ограничение мономорфизма с помощью {-# LANGUAGE NoMonomorphismRestriction #-}, или вы можете предоставить явного типа expr это держит его обобщенным:

main :: IO ()
main = print $ coerceInteger expr : expr
    where
        expr :: Expr a => a
        expr = literal 100

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


на самом деле, как только вы добавляете подпись типа, вы даже можете делать такие вещи, как

main :: IO ()
main = print $ coerceInteger expr : expr : expr : expr : expr : expr
    where
        expr :: Expr a => a
        expr = literal 100

без каких-либо проблем, это будет распечатать [100, 100, 100, 100, 100, 100]. Инициал coerceInteger необходим, потому что в противном случае компилятор не будет знать, что его инстанцировать как и, следовательно, не будет иметь Show экземпляр print.