我試圖定義一種可以限制爲Nat
的子集的類型。雖然我意識到一個簡單的解決方案是使用常規的ADT,但我仍然好奇這種類型是否可以通過附帶的FromJSON
實例來定義。這是我到目前爲止有:是否有可能爲我的奇怪枚舉類型聲明FromJSON實例?
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Test where
import Control.Monad
import Data.Aeson
import Data.Kind (Constraint)
import Data.Proxy
import Data.Type.Equality
import GHC.TypeLits
import Prelude
type family Or (a :: Bool) (b :: Bool) :: Bool where
Or 'False 'False = 'False
Or 'True 'False = 'True
Or 'False 'True = 'True
Or 'True 'True = 'True
type family IsOneOf (n :: Nat) (xs :: [Nat]) :: Bool where
IsOneOf n '[] = 'False
IsOneOf n (x ': xs) = Or (n == x) (IsOneOf n xs)
type family Member n xs :: Constraint where
Member n xs = 'True ~ IsOneOf n xs
data SomeEnum (xs :: [Nat]) where
SomeEnum :: (KnownNat n, Member n xs) => Proxy n -> SomeEnum xs
這可以再使用如下:
λ> SomeEnum (Proxy :: Proxy 1) :: SomeEnum [1,2]
我能夠定義一個ToJSON
實例:但是它不
instance ToJSON (SomeEnum xs) where
toJSON (SomeEnum n) = Number (fromIntegral . natVal $ n)
似乎聲明一個FromJSON
實例是可能的,因爲我不知道如何說服編譯器,無論我可能從JSON文檔中獲取的數字是否爲該集合的成員可接受的值爲SomeEnum
。
我的問題是 - 是否有可能根據數據類型的當前表達式來聲明該實例?也許類型本身可以以某種方式進行調整以允許這樣的情況,同時保持被限制在特定集合Nat
的行爲?
我不是很熟悉Haskell的類型級別的特徵,所以也許我所問的目前的形式沒有意義。我將不勝感激任何評論。
它應該是可能的,但它會很尷尬,並要求該'FromJSON'實例反映'爲了xs'到價值層面來驗證解析數是xs'的'的元素。這樣做需要能夠表達'xs'的每個元素都有'KnownNat'約束,這可以完成,但是會很尷尬。 – Carl
這可能是不可能的,因爲您試圖使用取決於JSON中的內容的類型來創建值。從理論上講,使用存在主義來說,它是*一些數字而不是任何具體數字應該避開這個問題,但我認爲你仍然需要提供一個見證,這需要依賴類型。 – Lazersmoke
@Lazersmoke SomeEnum是需要的存在。 'GHC.TypeLits'具有在運行時創建見證的必要機制,適合填充存在。 – Carl