Haskell reflection paketi ile oynuyordum ve tam olarak anlamadığım bir hata türüyle karşılaştım.Yansıma içeren bu gizemli Haskell tipi hatanın sebebi nedir?
{-# LANGUAGE TypeApplications #-}
reifyGood :: (forall s. Reifies s a => Proxy a) ->()
reifyGood p = reify undefined (\(_ :: Proxy t) -> p @t `seq`())
İlginçtir, ancak bu biraz farklı bir program yapar değil typecheck:
reifyBad :: (forall s. Reifies s s => Proxy s) ->()
reifyBad p = reify undefined (\(_ :: Proxy t) -> p @t `seq`())
• Could not deduce (Reifies s s) arising from a use of ‘p’
from the context: Reifies s a0
bound by a type expected by the context:
Reifies s a0 => Proxy s ->()
• In the first argument of ‘seq’, namely ‘p @t’
In the expression: p @t `seq`()
In the second argument of ‘reify’, namely
‘(\ (_ :: Proxy t) -> p @t `seq`())’
The First, mutlulukla typechecks aşağıdaki işlevi, yazmaya çalıştı ifadeleri aynıdır, ancak tür imzaları arasındaki farka dikkat edin:
Bu merakı buluyorum. İlk bakışta, bu geçersizdir çünkü ikinci örnekte, s
skolem kapsamından çıkar. Ancak, bu gerçek hata mesajı asla değil aslında bu biraz farklı programla aksine, Skolem kaçış bahseder: Başka
reifyBad' :: (forall s. Reifies s s => Proxy s) ->()
reifyBad' p = reify undefined (\(_ :: Proxy t) -> p @t) `seq`()
• Couldn't match expected type ‘t0’ with actual type ‘Proxy s’
because type variable ‘s’ would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context:
Reifies s a0 => Proxy s -> t0
• In the expression: p @t
In the second argument of ‘reify’, namely
‘(\ (_ :: Proxy t) -> p @t)’
In the first argument of ‘seq’, namely
‘reify undefined (\ (_ :: Proxy t) -> p @t)’
Yani belki burada bir şey oyunda yer almaktadır.
reify
tipini incelendiğinde, bir şeylerin yanlış olduğunu biraz açık olur: GHC onları izin vermeyeceğini mantıklı görünüyor böylece a
ve s
ait
reify :: forall a r. a -> (forall s. Reifies s a => Proxy s -> r) -> r
kapsamları, açıkça farklıdır birleştirmek. Bununla birlikte, Reifies
'daki işlevsel bağımlılığın getirdiği yerel eşitlik kısıtlamasıyla ilgili bir tür garip davranışa neden oluyor. İlginçtir, fonksiyonlar typechecks bu çifti:
foo :: forall a r. Proxy a -> (forall s. (s ~ a) => Proxy s -> r) -> r
foo _ f = f Proxy
bar :: (forall a. Proxy a) ->()
bar p = let p' = p in foo p' (\(_ :: Proxy s) -> (p' :: Proxy s) `seq`())
... ama onları bir Skolem kaçış hatasını üreten typecheck başarısız yapar foo
tipi imzası eşitlik sınırlama kaldırarak: Bu noktada
• Couldn't match type ‘a0’ with ‘s’
because type variable ‘s’ would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context:
Proxy s ->()
Expected type: Proxy s
Actual type: Proxy a0
• In the first argument of ‘seq’, namely ‘(p' :: Proxy s)’
In the expression: (p' :: Proxy s) `seq`()
In the second argument of ‘foo’, namely
‘(\ (_ :: Proxy s) -> (p' :: Proxy s) `seq`())’
, Ben şaşırdım. Bir çift (son derece ilgili) sorularım var.
Neden
reifyBad
ilk etapta typecheck başarısız?Daha spesifik olarak, eksik örneği neden hatası üretir? Ayrıca
, bu davranış, beklenen ve iyi tanımlanmış, veya sadece bu belirli bir sonucu olur typechecker garip bir kenarı olduğu?