2016-08-16 1 views
1

Können sagen wir folgendes haben:Einschränkende Instanzen

{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE ConstraintKinds #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE TypeFamilyDependencies #-} 

type family CategoryLikeT p_a_b = t | t -> p_a_b 

type IsCategoryLike p t a b = (t ~ CategoryLikeT (p, a, b)) 

class CategoryLike p where 
    (>>>) :: 
    (
     IsCategoryLike p t1 a b, 
     IsCategoryLike p t2 b c, 
     IsCategoryLike p t3 a c 
    ) => t1 -> t2 -> t3 

Wir haben dann feststellen, dass diese kompiliert fein:

f :: 
    (
    CategoryLike p, 
    IsCategoryLike p t1 a b, 
    IsCategoryLike p t2 b c, 
    IsCategoryLike p t3 c d, 
    IsCategoryLike p t4 a d 
) => t1 -> t2 -> t3 -> t4 
f x y z = x >>> y >>> z 

Aber wir haben keine Instanzen noch definiert. Lets tun:

data BasicFunction 
type instance CategoryLikeT (BasicFunction, a, b) = a -> b 

instance CategoryLike BasicFunction where 
    (>>>) = flip (.) 

Aber auch „Ints“ unter Zusatz Art Kategorie sind wie, wenn wir annehmen, nur „a“ und „b“ sind sowohl Void, zum Beispiel: Daten BasicInt Typ-Instanz CategoryLikeT (BasicInt, Void, Void) = Int

instance CategoryLike BasicFunction where 
    (>>>) = (+) 

natürlich ist die oben nicht funktioniert, weil es keine Einschränkungen ist auf „a“ oder „b“ in der Instanzdefinition, so gibt es keine Garantie >>> bekommt alle gleich Typ, daher (+) ist nicht allgemein genug. Also, was ich in Betracht gezogen wird wie folgt aktualisiert:

Erstens eine Einschränkungsart Zugabe:

type family CategoryConstraints p t a b 

Und dann das Hinzufügen der Definition von IsCategoryLike wie folgt aus:

type IsCategoryLike p t a b = 
    (t ~ CategoryLikeT (p, a, b), CategoryConstraints p t) 

Wir können dann hinzufügen die folgende Einschränkung:

type instance CategoryConstraints BasicInt t = (t ~ Int) 

Aber jetzt haben wir ein Problem. f nicht mehr funktioniert, diesen Fehler geben:

Could not deduce: CategoryConstraints p (CategoryLikeT (p, a, c))) 

Wir werden diese auf zwei Arten beheben:

Zum einen durch IsCategoryLike p t5 a c den Zwängen in f hinzufügen. Bei komplexeren Funktionen könnte dies jedoch sehr schnell unübersichtlich werden. Sie müssten für jede Operation eine Einschränkung hinzufügen. Auch geringfügige Änderungen wie das Ändern von (x >>> y) >>> z zu x >>> (y >>> z) erfordern eine Änderung der Signatur, die nicht erforderlich war, wenn die Einschränkungen nicht vorhanden waren.

Alternativ könnte die Typ-Signatur ganz weggelassen werden, oder Teil-Typ-Signaturen könnten verwendet werden.

Allerdings möchte ich vollständige Signaturen behalten, ohne dann zu wachsen und schwer zu warten. Können die Leute alternative Ansätze vorschlagen?

Antwort

3

Hmmm ... Ich bin mir nicht sicher, das ist der beste Ansatz, aber hier ist eine direkte Verbesserung von dem, was Sie haben. Insbesondere denke ich, dass die Verwendung zugehöriger Typen die Dinge sauberer macht ...

{-# LANGUAGE TypeFamilies, 
      ConstraintKinds, 
      FlexibleInstances, 
      TypeFamilyDependencies #-} 

import GHC.Exts (Constraint) 

class CategoryLike p where 
    type CategoryLikeT p a b = t | t -> p a b 
    type CategoryConstraints p a b :: Constraint 
    type CategoryConstraints p a b =() 
    (>>>) :: (CategoryConstraints p a b, CategoryConstraints p b c, CategoryConstraints p a c) 
    => CategoryLikeT p a b -> CategoryLikeT p b c -> CategoryLikeT p a c 

data BasicFunction 
instance CategoryLike BasicFunction where 
    type CategoryLikeT BasicFunction a b = a -> b 
    (>>>) = flip (.) 

data BasicInt 
instance CategoryLike BasicInt where 
    type CategoryLikeT BasicInt Int Int = Int 
    type CategoryConstraints BasicInt a b = (a ~ Int, b ~ Int) 
    (>>>) = (+) 

Also, das ist, was f wie sieht nun: (Ich bin es mit einem expliziten forall zu schreiben, weil, dass es einen Kandidaten macht TypeApplications für die Verwendung)

f :: forall p a b c d. (
    CategoryLike p, 
    CategoryConstraints p a b, 
    CategoryConstraints p b c, 
    CategoryConstraints p a c, 
    CategoryConstraints p a d, 
    CategoryConstraints p d b 
) => CategoryLikeT p a d -> 
     CategoryLikeT p d b -> 
     CategoryLikeT p b c -> 
     CategoryLikeT p a c 
f x y z = x >>> y >>> z 

, es zu benutzen, kann ich mach sowas (das sieht überraschend gut aus):

ghci> :set -XTypeApplications 
ghci> :t f @BasicFunction (+1) id show 
f @BasicFunction (+1) id show :: (Show a, Num a) => a -> [Char] 
ghci> :t f @BasicInt 1 2 3 
f @BasicInt 1 2 3 :: Int 
+0

Die Schlüsselfrage ist, dass du 'f' mit einer Typ-Signatur schreiben kannst? – Clinton

+0

@Clinton Immer noch keine großartige Signatur, aber ein bisschen besser. Zumindest kann es elegant mit sichtbaren Anwendungen verwendet werden – Alec

Verwandte Themen