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ć?
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ć?
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.
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. –
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.
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
Jest to możliwe, ale to nie ja :) Próbowałem ukryć swoją ignorancję przez bycie niejasnym. – chepner
Jest OK. Przynajmniej jest to dobry wskaźnik. Wykopam trochę głębiej – osager
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)}
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ę? –