2017-01-19 17 views
6

moim danychJak ograniczyć pole Int do zakresu wartości?

data User = User { uRank :: Int, uProgress :: Int } 

że chcemy ograniczać uRank z listy wartości [1, 1, 3], na przykład.

Jak to zrobić?

+4

Czy to nie jest proste, co można zrobić z enu m i użyć funkcji, która odwzorowuje ją na ints, jeśli chcesz na nich wykonać arytmetykę? –

Odpowiedz

9

Określenie typu małej sumy jest najlepszą odpowiedzią na to konkretne pytanie. Możesz również użyć newtype z inteligentnymi konstruktorami, aby osiągnąć ten efekt.

newtype Rank = UnsafeMkRank { unRank :: Int } 

mkRank :: Int -> Maybe Rank 
mkRank i 
    | i `elem` [-1, 1, 3] = Just (UnsafeMkRank i) 
    | otherwise = Nothing 

Teraz, pod warunkiem korzystania tylko bezpieczne mkRank konstruktora, można przyjąć, że wartości Rank mają wartości Int chcesz.

+1

Inteligentne konstruktory są często używane w połączeniu z listami eksportu modułów: moduł MyMod (Rank, myRank) gdzie .. nie eksportuje konstruktora UnsafeMkRank, uniemożliwiając korzystanie z niego w innych modułach, na wypadek gdybyś (lub inni użytkownicy) kiedykolwiek zapomnij, jak zrobić dobry Rank. –

8

Na coś tak małe, należy określić dokładny typ:

data Rank = RankNegOne | RankOne | RankThree -- There are probably better names 
data User = User { uRank :: Rank, uProgress :: Int } 

Haskell nie zmusza cię do kodowania wszystko jako podzbiór liczb całkowitych; wykorzystaj to!

Dla większych podzbiorów gdzie byłoby to trudne, szukasz utrzymaniu typu, które (jeszcze do niedawna?) Nie jest obsługiwany przez Haskell bezpośrednio.

+0

Czy można tutaj wyświetlić krótki przykład zależnego typu? Rzeczywiście istnieje dość wiele wartości dla wartości rangi. – osager

+1

Jest to możliwe, ale to nie ja :) Próbowałem ukryć swoją ignorancję przez bycie niejasnym. – chepner

+0

Jest OK. Przynajmniej jest to dobry wskaźnik. Wykopam trochę głębiej – osager

5

Ciecz Haskell, który dodaje typy udoskonalania na górze Haskell, może to zrobić. Zobacz odpowiednią sekcję samouczka: here.

Najpierw określ typ danych jako normalny.

module User where 

data User = User { uRank  :: Int 
       , uProgress :: Int 
       } 

Następnie definiujemy twoje ograniczenie jako typ udoskonalenia. Użytkownicy Liquid Haskell komentują adnotacje o składni {[email protected] blah @-}. Będziemy zdefiniować dziwne ograniczenie -1, 1, 3 pierwszy z typem RestrictedInt:

{[email protected] type RestrictedInt = {v : Int | v == -1 || v == 1 || v == 3} @-} 

Oznacza to, że RestrictedInt jest Int że jest albo -1, 1 lub 3. Zauważ, że możesz łatwo pisać zakresy i inne ograniczenia, nie musi to być bezmyślne wyliczanie prawowitych wartości.

Teraz ponownie określić typ danych w naszym języku wyrafinowanie przy użyciu tego typu int ograniczony:

{[email protected] data User = User { uRank  :: RestrictedInt 
        , uProgress :: Int } 
    @-} 

ta jest podobna do definicji ale z typem restrykcyjnym, a nie tylko Int. Moglibyśmy wprowadzić ograniczenie zamiast mieć alias typu, ale wtedy twój typ danych byłby całkiem nieczytelny.

Można zdefiniować dobre wartości i narzędzie liquid nie będzie narzekać:

goodValue :: User 
goodValue = User 1 12 

Ale złe wartości powodują błąd:

badValue :: User 
badValue = User 10 12 

$ liquid so.hs (lub integracja edytora, o ile trzeba, że setup) produkuje:

**** RESULT: UNSAFE ************************************************************ 


so.hs:16:12-18: Error: Liquid Type Mismatch 

16 | badValue = User 10 12 
       ^^^^^^^ 

    Inferred type 
    VV : {VV : GHC.Types.Int | VV == ?a} 

    not a subtype of Required type 
    VV : {VV : GHC.Types.Int | VV == (-1) 
           || (VV == 1 
            || VV == 3)} 

    In Context 
    ?a := {?a : GHC.Types.Int | ?a == (10 : int)}