Mam funkcję użytkową, który wylicza wszystkie wartości typu, który jest zarówno przeliczalny i ograniczony:Generowanie listy Ints powiązany z typem Enum
enumerate :: (Enum a, Bounded a) => [a]
enumerate = [minBound .. maxBound]
i typ danych, który obejmuje mapowania przeliczalny typy do liczb całkowitych :
data Attribute a = Attribute { test :: a -> Int
, vals :: [Int]
, name :: String }
Gdzie vals
lista liczb całkowitych reprezentujących wszystkie możliwe wartości przeliczalny. Na przykład, gdybym miał
data Foo = Zero | One | Two deriving (Enum,Bounded)
następnie vals
byłoby [0,1,2]
.
Chcę móc tworzyć te atrybuty programowo, korzystając tylko z funkcji odwzorowującej numer a
na typ przeliczalny i nazwę. Coś takiego:
attribute :: (Enum b, Bounded b) => (a -> b) -> String -> Attribute a
attribute f str = Attribute (fromEnum . f) vs str
where
vs = map fromEnum enumerate
nie typecheck, ponieważ nie ma możliwości podłączenia wezwanie do enumerate
z b
w podpisie typu. Więc pomyślałem, mogłem to zrobić:
vs = map fromEnum $ enumerate :: [b]
ale nie kompiluje albo - kompilator zmienia nazwę że b
do b1
. Starałem się być mądrzejszy, używając rozszerzenia GADTs:
attribute :: (Enum b, Bounded b, b ~ c) => {- ... -}
vs = map fromEnum $ enumerate :: (Enum c,Bounded c) => [c]
Ale znowu, c
zostaje zmieniona na c1
.
Nie chcę aby to rodzaj b
jako parametr w Attribute
typu (głównie dlatego, że chcę, aby przechowywać listy atrybutów z potencjalnie różnymi wartościami b
- Dlatego test
ma typ a -> Int
i vals
ma typ [Int]
).
Jak mogę napisać ten kod, aby zrobił to, co chcę?
scoped Rodzaj Zmienne działa perfecly, dzięki! –
Po raz pierwszy widzę niezdefiniowane 'używane do wykonania użytecznego zadania. –
@ChrisTaylor: Mógłbyś oczywiście użyć innej specjalizacji 'const', takiej jak' asTypeOf2 :: [b] -> (a -> b) -> [b] ', a następnie możesz napisać wyliczenie \' asTypeOf2 \ 'f', ale prawdopodobnie nie jest to warte wysiłku. – Vitus