2016-09-08 41 views
8

Amacım bu gibi terimlerden () ortadan kaldırmaktır:Tip azaltma sonsuz döngü

(a, b)  -> (a, b) 
((), b)  -> b 
(a, ((), b)) -> (a, b) 
... 

Ve bu kod şudur: bunu deneyerek, Ancak

{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE TypeOperators #-} 
{-# LANGUAGE UndecidableInstances #-} 

module Simplify where 

import Data.Type.Bool 
import Data.Type.Equality 

type family Simplify x where 
    Simplify (op() x) = Simplify x 
    Simplify (op x()) = Simplify x 
    Simplify (op x y) = If (x == Simplify x && y == Simplify y) 
          (op x y) 
          (Simplify (op (Simplify x) (Simplify y))) 
    Simplify x   = x 

:

:kind! Simplify (String, Int) 

... tip denetleyicide sonsuz bir döngüye yol açar. Ben If tipi ailenin indirgenemez terimlerle ilgilenmesi gerektiğini düşünüyorum, ama açıkçası bir şey eksik. Ama ne?

+4

, gerekli olmadıkça 'If' ikinci dal değerlendirmeye alınmayacaktır dolayısıyla söyledi. Bu varsayımın gereksiz olduğunu düşünüyorum. –

+6

Ve bu arada: 'op' üzerinden polimorfik olmak muhtemelen yanlıştır. Örneğin, 'Basitleştir (Either() Int)' muhtemelen 'Int''e indirmemelidir. –

+0

Davranışta ne olmalı? (String,(), Int) '? Her iki çözüm de şimdiye kadar bunu önerdi ki 'Int'. Hatta (String, Int) 'ın mümkün olup olmadığını bile bilmiyorum. – gallais

cevap

10

Tip ailesi değerlendirmesi tembel değildir, bu yüzden If c t f tüm c, t ve f öğelerini değerlendirir. (Aslında, aile değerlendirme sırası şu anda gerçekten tanımlanmamıştır.) Sonsuz bir döngü ile sona erdirmek hiç de şaşırtıcı değil - Simplify (op x y) olsa bile, her zaman Simplify (op (Simplify x) (Simplify y))'u değerlendirin!

Çok gibi ayrı Rekursif basitleştirilmesi bölerek bu önleyebilirsiniz:

{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE TypeOperators #-} 
{-# LANGUAGE UndecidableInstances #-} 

module Simplify where 

import Data.Type.Bool 
import Data.Type.Equality 

type family Simplify1 x where 
    Simplify1 (op() x) = x 
    Simplify1 (op x()) = x 
    Simplify1 (op x y) = op (Simplify1 x) (Simplify1 y) 
    Simplify1 x   = x 

type family SimplifyFix x x' where 
    SimplifyFix x x = x 
    SimplifyFix x x' = SimplifyFix x' (Simplify1 x') 

type Simplify x = SimplifyFix x (Simplify1 x) 

fikirdir:

  1. Simplify1 basitleştirme bir adım yok.
  2. SimplifyFix eşitse kontrol eder x ve tek aşamalı bir basitleştirme x' alır ve bunlar değilse (böylece Simplify1 sabit nokta bulma) basitleştirme bir adım yok.
  3. SimplifySimplify1 numaralı çağrıyla SimplifyFix zincirinden çıkar. eşleşen tipi ailesi desen tembel olduğundan

, SimplifyFix doğru sonsuz döngüler önlenmesi, değerlendirme geciktirir.

Ve gerçekten:

*Simplify> :kind! Simplify (String, Int) 
Simplify (String, Int) :: * 
= (String, Int) 

*Simplify> :kind! Simplify (String, ((), (Int,()))) 
Simplify (String, ((), (Int,()))) :: * 
= ([Char], Int) 
+3

Vay, ben de bir cevap yazıyordum ... ve ikinci test davası için * aynı seçenekle geldim.Değeri için, cevabın biraz daha basit olduğunu düşünüyorum, belki daha az genellenebilir: 'Simplify1 ((), y) = y; Simplify1 (x,()) = x; Simplify1 diğer = diğer; Basitleştirin (x, y) = Basitleştirin1 (Basitleştirin x, Basitleştirin y); Diğerlerini basitleştir = diğer '. –

+0

@DanielWagner Demek istediğim, gerçek bir test için en az iki tane derin (derin) içmeniz gerekiyor, değil mi? :-) Ve çözümünüzü karşılaştırmak güzel! –

+3

Ayrıca, eğlenceli bir sihir: ': tür! forall a. Simplify1 (a,()) = a'. Bu, bazı basit zekânı gerektirir, zira “Simplify1” in ilk iki cümlesi de aynı cevabı verecektir! –

3

Bir fixpoint karıştığı bu karmaşık çözümü oluşturmak için gerek yoktur, bunu sadeleştirme bir kat yapısına sahip olduğu göz önüne alındığında belirtmekte fayda var ifadesini yeniden erişir tekrar ve tekrar.

Bu sadece yeterli olacaktır: Bu size o tip düzeyinde hesaplama tembel varsayıyoruz görünüyor

{-# LANGUAGE TypeFamilies   #-} 
{-# LANGUAGE UndecidableInstances #-} 
module Simplify where 

type family Simplify x where 
    Simplify (op a b) = Op op (Simplify a) (Simplify b) 
    Simplify x  = x 

type family Op op a b where 
    Op op() b = b 
    Op op a() = a 
    Op op a b = op a b