2017-02-20 3 views
3

Ich habe eine Reihe von komplizierten Art Level-Funktionen, die Dinge wie bewerten:Infer Einschränkungen des Typs Familien von Zwängen der Argumente

(If (EqNat n 2) 
    1 
    (If (EqNat n 1) 
    2 
    (If (EqNat n 0) 3 0))) 

Jetzt offensichtlich in diesem Fall ist dieser Ausdruck ein KnownNat. Mehr im Allgemeinen können wir sagen:

forall (c :: * -> Constraint) (p :: Bool) a b . 
(c a, c b) => c (If p a b) 

Gibt es eine Möglichkeit GHC zu lehren, diese zu schließen?

Edit: @chi darauf hingewiesen, dass in einigen Fällen diese mit GADTs auflösbar ist aber mein Fall ist diese:

module M1 (C(..)) where 

type familiy NestedIfs (n :: Nat) :: Nat 
type NestedIfs n = <<complex nested ifs like the above that evals to literals>> 

class C a (n :: Nat) where 
    f :: KnownNat n => a -> NestedIfs n -> Bool 

und dann

module M2() where 
import M1 

instance C Int n where 
    f = ...require that KnownNat (NestedIfs n)... 

NestedIfs nicht zugänglich M2 ist aber vielleicht sollte GHC in der Lage sein, , dass forall n . KnownNat n => KnownNat (NestedIfs n) aus der allgemeinen Schlussfolgerung, die ich oben erwähne.

+1

Wir brauchen 'forall (p :: Bool). Entweder (p: ~: True) (p: ~: False) 'dafür. Ich denke nicht, dass man das ohne Singleton-Argument für "p :: Bool" erreichen kann. – chi

+0

Könnten Sie das näher ausführen? 1) Welche anderen Arten bewohnen die 'Bool'-Art, 2) Wenn 'Entweder (p: ~: True) (p: ~: False)' ein Ende der Bool-Art war, würde uns das auf der technischen Ebene tatsächlich helfen Hilft GHC, automatisch zu folgern, wonach ich gefragt habe? – fakedrake

+0

Nebenbei bemerkt vielleicht [diese Frage fragte ich neulich] (http://stackoverflow.com/questions/42240533/infer-constraints-for-both-if-and-else-of-type-equality) – fakedrake

Antwort

4

Diese Frage ist nicht schwer, aber schlecht gestellt. Welchen Wert erwarten Sie vom Typ c (If p a b) :: Constraint? Was Sie wahrscheinlich fragen wollen, ist, wie in den Körper dieses

bisect :: forall b c x y. SingI b => Proxy b -> (c x, c y) :- c (If b x y) 

Hier zu füllen, wie in den Kommentaren erwähnt, ich c bin zwingt ein Singleton zu sein, so dass ich Either (c :~: True) (c :~: False) (Sie lesen kann erhalten kann meine SingI Constraint als Durchsetzung, dass c :: Bool muss entweder True oder False sein, die ist leider keine triviale Anfrage, wenn auf der Ebene des Typs seit Any hat Art Bool zu). Das kommt aus dem constraints Paket. Es ist eine Art zu sagen, dass die Bedingung (a,b) die Einschränkung If c a b impliziert. Das ist genau wie Sie Ihre Anfrage ausdrücken - Sie wollen einen Beweis, dass zwei sagt gegeben c x und c y halten, c (If b x y) wird auch halten.

Füllung im Körper dieser Funktion ist eigentlich sehr wenig Code:

{-# LANGUAGE DataKinds, TypeFamilies, ConstraintKinds, TypeOperators, RankNTypes, 
    ScopedTypeVariables, PolyKinds #-} 

import Data.Constraint 
import Data.Singletons.Prelude hiding ((:-)) 

bisect :: forall b c x y. (SingI b) => Proxy b -> (c x, c y) :- c (If b x y) 
bisect _ = unmapDict $ case sing :: Sing b of 
         STrue -> mapDict weaken1 
         SFalse -> mapDict weaken2 
+0

Thnx! Gibt es eine Möglichkeit, "Proxy b" loszuwerden? – fakedrake

+1

@fakedrake Ja, Sie können dieses Argument einfach löschen (und AllowAmboundyTypes aktivieren). Um "bisect" zu verwenden, muss dann "TypeApplication" aktiviert sein. – Alec

Verwandte Themen