2016-07-20 6 views
0

Ein Teil der Macht von Haskell besteht darin, Bereichsüberprüfungen an das Typsystem zu delegieren (siehe zum Beispiel Numeric.Natural). Ist dies bei Typen möglich, deren Werte zur Laufzeit einmal definiert sind? Ich würde gerne ein Enum, dessen Werte zur Kompilierzeit unbekannt sind.Haskell: endliche Arten von benutzerdefinierten Werten?

Edit: In Bezug auf die Beispiel-Anwendung:

-- Defines the list of allowed values 
init :: [a] -> ? 

-- Constructs a new instance 
construct :: a -> ? -> Maybe Foo 

-- Then just usable like an enum 
bar :: Int -> Foo -> Bar 

Im Idealfall würde ich in der Lage sein, die Dinge zu verwenden, wie Bounded auf sie zu.

+0

gut Typen zur Laufzeit gelöscht werden (können Sie ein Beispiel geben - weil nicht ist, was Sie sind nur eine Art von 'isElem Wert Set' fragen, wo' Set' zur Laufzeit definiert?) Im Grunde – Carsten

+0

Dies ist das Problem, das ich habe. Ich dachte, es könnte möglich sein, eine interne Repräsentation als ([erlaubt], Vielleicht Wert) zu haben, aber es hat keine sehr schöne Semantik. –

+1

Können Sie mithilfe dieser hypothetischen Funktion einen imaginären Code anzeigen? Sprechen Sie über so etwas wie abhängige Typen? –

Antwort

2

Ihr Beispielcode ist leider zu spärlich, um anzuzeigen, was Sie wirklich meinen. Ich vermute, dass Sie nach abhängigen Typen sind, wie n.m. suggested. Wenn das der Fall ist, ist es wahrscheinlich besser, etwas wie Agda anstatt Haskell anzusehen. Wenn Sie eine etwas sicherere Version von was Daniel Wagner suggested möchten, können Sie es mit dem reflection Paket erhalten.

{-# LANGUAGE ScopedTypeVariables #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE UndecidableInstances #-} 
{-# LANGUAGE RankNTypes #-} 
{-# LANGUAGE GeneralizedNewtypeDeriving #-} 
{-# LANGUAGE StandaloneDeriving #-} 

module DynEnum (.... not including newtype constructors) where 

import Data.Reflection 
import Data.Proxy 
import Data.Set (Set, splitMember, size, lookupIndex, fromList, elemAt, member, findMin, findMax) 
import Data.Foldable 
import Data.Bool 
import Data.Type.Coercion 

-- Just Enum 
newtype Limited a s = Limited { unLimited :: a } 

type role Limited representational nominal 

-- We can safely conflate types of values that come 
-- from the same set. 
coerceLimited :: (Reifies s (Set a), Reifies t (Set a), Ord a) 
       => Maybe (Coercion (Limited a s) (Limited a t)) 
coerceLimited 
    | reflect (Proxy :: Proxy s) == reflect (Proxy :: Proxy t) 
     = Just Coercion 
    | otherwise = Nothing 

instance (Ord a, Reifies s (Set a)) => Enum (Limited a s) where 
    toEnum i 
    | 0 <= i && i < size values = Limited $ elemAt i values 
    | otherwise = error "Limited toEnum: out of range" 
    where values = reflect (Proxy :: Proxy s) 
    fromEnum x = case lookupIndex (unLimited x) (reflect x) of 
       Nothing -> error "Limited fromEnum: out of range" 
       Just i -> i 
    enumFrom (Limited a) = case splitMember a (reflect (Proxy :: Proxy s)) of 
       (_, False, s) -> fmap Limited $ toList s 
       (_, True, s) -> Limited a : fmap Limited (toList s) 
    enumFromTo (Limited a) (Limited b) = case splitMember a (reflect (Proxy :: Proxy s)) of 
       (_, inclFirst, s) -> case splitMember b s of 
        (t, inclLast, _) -> bool id (Limited a:) inclFirst 
             . (map Limited (toList t) ++) 
             $ bool [] [Limited b] inclLast 

initialize :: Ord a 
      => [a] 
      -> (forall s . Enum (Limited a s) => Proxy s -> r) 
      -> r 
initialize vals f = reify (fromList vals) f 

construct :: forall s a . (Ord a, Reifies s (Set a)) => a -> Maybe (Limited a s) 
construct x 
    | x `member` reflect (Proxy :: Proxy s) = Just (Limited x) 
    | otherwise = Nothing 

newtype Bound a b = Bound a deriving (Enum) 

type role Bound representational nominal 

instance Reifies b (a, a) => Bounded (Bound a b) where 
    minBound = Bound . fst $ reflect (Proxy :: Proxy b) 
    maxBound = Bound . snd $ reflect (Proxy :: Proxy b) 

initializeBounded :: (a, a) 
        -> (forall b . Bounded (Bound a b) => Proxy b -> r) 
        -> r 
initializeBounded bounds f = reify bounds f 

newtype LimitedB a s b = LimitedB (Bound (Limited a s) b) 

deriving instance (Ord a, Reifies s (Set a)) => Enum (LimitedB a s b) 
deriving instance Reifies b (Limited a s, Limited a s) => Bounded (LimitedB a s b) 

initializeLimitedB :: Ord a 
        => [a] 
        -> (forall s b . (Enum (LimitedB a s b), Bounded (LimitedB a s b)) => Proxy s -> Proxy b -> r) 
        -> r 
initializeLimitedB [] _f = error "Cannot initialize LimitedB with an empty list" 
initializeLimitedB vals f = reify set $ \ps -> 
        reify (Limited (findMin set), Limited (findMax set)) $ \pb -> 
        f ps pb 
    where 
    set = fromList vals 
+0

Sorry für die Sparsity; Ich finde es schwierig, ein Konzept auszudrücken. Nach einem schnellen Google scheint es, dass ich abhängige Typen möchte. In diesem Fall diejenigen, deren Wertegruppe von etwas abhängt, das zur Laufzeit definiert ist. Ich werde deine Antwort jedoch akzeptieren, da ich glaube, dass es so nah wie möglich an meinem Ziel in Haskell liegt. Danke für Ihre Hilfe. –

+1

@TomJohnson, können Sie in der Lage sein, zu tun, was Sie in Haskell mit Singletons wollen; Es wird nur ziemlich hässlich und wahrscheinlich ineffizient sein. Der Trick besteht darin, eine Typ-Level-Repräsentation einer Menge von Werten zu erstellen und dann eine GADT zu verwenden, um diese auf die Termebene herunter zu bringen. Sehen Sie sich [Data.Type.Set] (http://hackage.haskell.org/package/type-level-sets-0.7/docs/Data-Type-Set.html) in den type-level-sets an 'Paket für ein relativ einfaches (aber immer noch ziemlich kniffliges!) Beispiel. – dfeuer

1

Vielleicht ist ein Set für Ihre Bedürfnisse geeignet. Wir haben:

initialize :: Ord a => [a] -> Set a 
initialize = fromList 

construct :: Ord a => a -> Set a -> Maybe a 
construct x xs = guard (x `member` xs) >> return x 

dynamicMinBound :: Set a -> Maybe a 
dynamicMinBound xs = fst <$> minView xs 

dynamicMaxBound :: Set a -> Maybe a 
dynamicMaxBound xs = fst <$> maxView xs 

enumerate :: Set a -> [a] 
enumerate = toList 

dynamicToEnum :: Int -> Set a -> Maybe a 
dynamicToEnum n xs = guard (inRange n (0, size xs-1)) >> return (elemAt n xs) 

dynamicFromEnum :: Ord a => a -> Set a -> Maybe Int 
dynamicFromEnum = lookupIndex 

Ich glaube, das die Operationen deckt Sie gefragt, ob ich einfach etwas falsch verstanden haben könnte - Ihre Spezifikation ist nicht 100% klar für mich.

Verwandte Themen