여기에 언급 된 것과 달리, 이러한 유형을 정의하는 것은 완벽하게 의미가 있습니다 (실제로는 올바른 라이브러리 - generics-sop
).
{-# LANGUAGE PatternSynonyms, PolyKinds, DeriveGeneriC#-}
import Generics.SOP
import qualified GHC.Generics as GHC
import Data.Dependent.Sum
data Tup2List :: * -> [*] -> * where
Tup0 :: Tup2List() '[]
Tup1 :: Tup2List x '[ x ]
TupS :: Tup2List r (x ': xs) -> Tup2List (a, r) (a ': x ': xs)
newtype GTag t i = GTag { unTag :: NS (Tup2List i) (Code t) }
유형 GTag
당신이 Magic
을 부르는 : 기본적으로 모든 기계는 이미이 라이브러리에 의해 제공됩니다. 실제 '마법'은 유형 목록의 목록으로 유형의 제네릭 표현을 계산하는 Code
유형 모음에서 발생합니다. 유형 NS (Tup2List i) xs
은 정확히 xs
중 하나 인 Tup2List i
을 보유한다는 것을 의미합니다. 이는 인수 목록이 일부 튜플과 동형임을 증명하는 것입니다.당신이 필요로하는
모든 클래스를 유도 할 수있다 :
pattern TagFoo ::() => (x ~ Int) => GTag SomeUserType x
pattern TagFoo = GTag (Z Tup1)
pattern TagBar ::() => (x ~ Char) => GTag SomeUserType x
pattern TagBar = GTag (S (Z Tup1))
pattern TagBaz ::() => (x ~ (Bool, String)) => GTag SomeUserType x
pattern TagBaz = GTag (S (S (Z (TupS Tup1))))
및 간단한 테스트 :
fun0 :: GTag SomeUserType i -> i -> String
fun0 TagFoo i = replicate i 'a'
fun0 TagBar c = c : []
fun0 TagBaz (b,s) = (if b then show else id) s
fun0' = \(t :& v) -> fun0 t v
main = mapM_ (putStrLn . fun0' . toTagVal)
[ Foo 10, Bar 'q', Baz True "hello", Baz False "world" ]
data SomeUserType = Foo Int | Bar Char | Baz Bool String
deriving (GHC.Generic, Show)
instance Generic SomeUserType
당신은이 유형에 대한 유효한 태그에 대한 몇 가지 패턴 동의어를 정의 할 수 있습니다
제네릭 형식 함수로 표현되므로 t보다 일반적인 연산을 작성할 수 있습니다 ags. 예를 들어, exists x . (GTag t x, x)
는 Generic t
에 대한 t
에 동형 :
type GTagVal t = DSum (GTag t) I
pattern (:&) :: forall (t :: * -> *).() => forall a. t a -> a -> DSum t I
pattern t :& a = t :=> I a
toTagValG_Con :: NP I xs -> (forall i . Tup2List i xs -> i -> r) -> r
toTagValG_Con Nil k = k Tup0()
toTagValG_Con (I x :* Nil) k = k Tup1 x
toTagValG_Con (I x :* y :* ys) k = toTagValG_Con (y :* ys) (\tp vl -> k (TupS tp) (x, vl))
toTagValG :: NS (NP I) xss -> (forall i . NS (Tup2List i) xss -> i -> r) -> r
toTagValG (Z x) k = toTagValG_Con x (k . Z)
toTagValG (S q) k = toTagValG q (k . S)
fromTagValG_Con :: i -> Tup2List i xs -> NP I xs
fromTagValG_Con i Tup0 = case i of {() -> Nil }
fromTagValG_Con x Tup1 = I x :* Nil
fromTagValG_Con xs (TupS tg) = I (fst xs) :* fromTagValG_Con (snd xs) tg
toTagVal :: Generic a => a -> GTagVal a
toTagVal a = toTagValG (unSOP $ from a) ((:&) . GTag)
fromTagVal :: Generic a => GTagVal a -> a
fromTagVal (GTag tg :& vl) = to $ SOP $ hmap (fromTagValG_Con vl) tg
필요
Tup2List
을 위해, 그것은 당신이 태그로 두 개의 인수 (
Baz Bool String
)의 생성자를 나타내는 단순히 이유로 필요에 관해서는
귀하의 예에서는 튜플이 (Bool, String)
이상입니다.
또한 이기종 목록으로 인수를 나타내거나 더 간단하게
newtype GTag t i = GTag { unTag :: NS ((:~:) i) (Code t) }
type GTagVal t = DSum (GTag t) HList
fun0 :: GTag SomeUserType i -> HList i -> String
fun0 TagFoo (I i :* Nil) = replicate i 'a'
fun0 ...
그러나, 튜플 표현이 단항 튜플는 장점을 가지고
type HList = NP I -- from generics-sop
data Tup2List i xs where Tup2List :: Tup2List (HList xs) xs
로 구현할 수 튜플에있는 단일 값 (예 : (x,())
대신)에 '투영'됩니다. 명백한 방법으로 arguements를 표현하면 fun0
과 같은 함수는 생성자에 저장된 단일 값을 검색하기 위해 패턴 일치가 필요합니다.
"실제로 최상위 수준의 선언은 신경 쓰지 않습니다."- 형식을 선언하지 않고 만들려면 어떻게해야합니까? –
@ BenjaminHodgson 나는't '의 정의 안에서 어떻게 든 "파고 들며"연관된'Tag ...'와 같은 형태를 만들어내는'Magic t a'라는 선언을 원한다고 생각합니다. 필자는 파라 메트릭 다형성을 깨지 않으면 서 이것이 가능하다고 생각하지 않는다. 하나는't' 정의 안에서 일종의 반성이 필요할 것이다. – chi
기본적으로 chi가 말했듯이,'data TagSomeUserType a ... '라고 말하는 대신에 완전한 정의를 제공하기 위해'type TagSomeUserType a = Magic SomeUserType'을 말하고 싶습니다. – ajp