Итак, в моем текущем проекте я нахожу, что я делаю кучу логики уровня типа с одноточечными типами.Замена однотипных типов данных с помощью семейства данных
Например:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE GADTs #-}
module TypeBools where
type family (||) (a :: Bool) (b :: Bool) :: Bool where
'False || 'False = 'False
'False || 'True = 'True
'True || 'False = 'True
'True || 'True = 'True
data OrProof (a :: Bool) (b :: Bool) (c :: Bool) where
OrProof :: SBool (a || b) -> OrProof a b (a || b)
data SBool (b :: Bool) where
SFalse :: SBool 'False
STrue :: SBool 'True
class Boolean b where
sBool :: SBool b
instance Boolean 'False where
sBool = SFalse
instance Boolean 'True where
sBool = STrue
orProof :: (Boolean a, Boolean b) => OrProof a b (a || b)
orProof = go sBool sBool where
go :: SBool a -> SBool b -> OrProof a b (a || b)
go SFalse SFalse = OrProof SFalse
go SFalse STrue = OrProof STrue
go STrue SFalse = OrProof STrue
go STrue STrue = OrProof STrue
И это работает очень хорошо для меня. Мне не нравится кататься по одиночным путям вручную, имея возможность вызвать их при необходимости через класс (например, класс Boolean
), но это привело к появлению довольно похожих моделей , которые существуют только для того, чтобы тип как одноточечные данные.
Я подумал, что я мог бы абстрактное эти несколько классов типов в одной семье типа, , например, замену SBool
и Boolean
выше с:
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ConstraintKinds #-}
-- ...
class Singleton (t :: k) where
data Sing t
sing :: Sing t
instance Singleton 'False where
data Sing 'False = SFalse
sing = SFalse
instance Singleton 'True where
data Sing 'True = STrue
sing = STrue
type SBool b = Sing (b :: Bool)
type Boolean b = Singleton (b :: Bool)
sBool :: Boolean b => SBool b
sBool = sing
Но тогда я получаю шаблоны ошибок матча:
TypeBools2.hs:42:13:
Couldn't match type ‘b1’ with ‘'True’
‘b1’ is a rigid type variable bound by
the type signature for
go :: SBool a1 -> SBool b1 -> OrProof a1 b1 (a1 || b1)
at TypeBools2.hs:40:9
Expected type: SBool b1
Actual type: Sing 'True
Relevant bindings include
go :: SBool a1 -> SBool b1 -> OrProof a1 b1 (a1 || b1)
(bound at TypeBools2.hs:41:3)
In the pattern: STrue
In an equation for ‘go’: go SFalse STrue = OrProof STrue
In an equation for ‘orProof’:
orProof
= go sBool sBool
where
go :: SBool a -> SBool b -> OrProof a b (a || b)
go SFalse SFalse = OrProof SFalse
go SFalse STrue = OrProof STrue
go STrue SFalse = OrProof STrue
go STrue STrue = OrProof STrue
Я не уверен, есть ли что-то еще, я могу убедить компилятор в том, что b1
должен иметь вид Bool
, или если Я просто лаем по неправильному дереву здесь.
Могу ли я спросить, почему ваш '' || не кратко- схема? Я ожидал бы лучшего сокращения с помощью 'False || x = x; «Истина || x = 'True', например. – dfeuer
Вы должны просто использовать ['singleletons'] (https: // hackage.haskell.org/package/singletons-2.0.1/docs/Data-Singletons.html), но если вы не собираетесь его использовать, вы должны увидеть, как они это делают и скопировать (потому что они делают именно то, что вы хотите). В частности, вам необходимо семейство данных семейства * * * * связанных данных Sing (t :: k); class Singleton (t :: k), где sing :: Sing t' и 'data instance Sing (b :: Bool), где ...' – user2407038
dfeuer: он мог бы. Это, тем не менее, вопрос. – rampion