2017-12-08 96 views
8

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.

  1. Neden reifyBad ilk etapta typecheck başarısız?

  2. 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?

cevap

6
reify :: forall a r. a -> (forall s. Reifies s a => Proxy s -> r) -> r 

Skolem gereksinimi esas türü r yukarıda İkinci değişken bir miktarı olan s bağımlı olmayan belirtir. Aksi takdirde, reifyr döndürdüğü için gerçekten de kapsamını "kaçar".

reifyBad :: (forall s. Reifies s s => Proxy s) ->() 
reifyBad p = reify undefined (\(_ :: Proxy t) -> p @t `seq`()) 

olarak

biz türü r() olan bu işlev, dönüş türü olabilir, böylece reify ikinci bağımsız değişken, \(_ :: Proxy t) -> p @t `seq`() olduğunu görüyoruz. r ~(), s'a bağlı olmadığı için, burada bir sorun yok. Ancak, p türüne göre p @tReifies t t gerektirir. reify, t ~ s'u seçeceğinden, kısıtlama Reifies s s ile aynıdır. Bunun yerine, reify, yalnızca a'un undefined türünde olduğu Reifies s a sağlar. Burada

ince nokta undefined türlü a üretebilir olurken, tip denetleyicisi s ve a birleştirmek edemez olmasıdır. Bunun nedeni, reify ile aynı türde bir fonksiyonun, bir sabit (sert) tipte a sadece bir değerini alma hakkına sahip olması ve daha sonra istediği kadar çok sayıda s seçmesidir. Bu s s'yi tek bir a ile birleştirmek yanlış olabilir ve s seçimini reify ile sınırlandırabilir.

Bunun yerine, varyant burada r

reifyBad' :: (forall s. Reifies s s => Proxy s) ->() 
reifyBad' p = reify undefined (\(_ :: Proxy t) -> p @t) `seq`() 

içinde Proxy t, t ~ s olan \(_ :: Proxy t) -> p @t dönüş türü, olduğu anlaşılmaktadır. r ~ Proxy s, s'a bağlı olduğundan, bir skolem hatası tetikliyoruz.