2014-07-15 44 views
6

Şu anda Haskell'i öğreniyorum ve tipecilerin nasıl değerlendirildiğini ve let ve where'un nasıl çalıştığını anlamaya çalışıyorum. Bu kod iyi çalışır:Değerlendirmeler, nerede ve nerede Haskell

{-# 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] 

ama

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

ana işlevi değişen bir derleyici hatası neden olur:

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 

bunun ne tahmin ediyorum, çünkü ilk main yönteminde literal 100 iki kez değerlendirilirken, ikinci örnekte literal 200 sadece bir kez değerlendirilir ve böylece derleyici bir tür seçmeye zorlanır.

Bu hataya neden olmadan, kendimi tekrar etmekten kaçınmak için bu kodu nasıl belirleyebilirim? let expr = literal 300 in ... kullanmayı denedim, ancak aynı sorunla karşılaştı.

cevap

9

Sorun, ilk örneğinizle literal 200'un iki farklı bağlamda farklı yorumlanmasıdır. Sadece türlerini kapalı tabanlı

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

, derleyici o coerceInteger geçirilen çünkü o tip Integer değerine almak zorundadır yana ilk literal 100, tip Integer olması gerektiğini belirler olarak düşünün. Bu ayrıca (:) türünü Integer -> [Integer] -> [Integer] olmalıdır ve son literal 100 türünün [Integer] türüne sahip olması gerektiğini belirtir.

İkinci örnekte, her ikisinin de aynı değere ve dolayısıyla aynı türe sahip olduğunu söylüyorsunuz, ki bu imkansızdır çünkü ikinci, (:) yazım denetimi için bir liste olmalıdır. Bu aslında korkulan Monomorfizm kısıtlaması nedeniyle gerçekleşir. , One {-# LANGUAGE NoMonomorphismRestriction #-} ile monomorfizm kısıtlaması kapatın ya da bunu genelleştirilmiş tutar expr için açık tip sağlayabilir:: Bunu iki şekilde bu sorunu çözebilirsiniz bu yaklaşımların

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

Ya çalışır ve karar ne olursa olsun Bu sorunları önlemek için her zaman tip imzaları sağlamanızı öneririm. Tür imza eklemek kez


Aslında, hatta herhangi bir sorun olmadan

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

gibi şeyler yapabilirsiniz, bu [100, 100, 100, 100, 100, 100] basar. İlk coerceInteger başlangıç ​​olsa da, aksi takdirde derleyici, neyi örneklendireceğini bilmez ve bu nedenle print için Show örneğine sahip olmaz.