Tout d'abord, un peu passe-partout:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances, UndecidableSuperClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ScopedTypeVariables #-}
module ConstrainApplications where
import GHC.Exts (Constraint)
import Data.Type.Equality
Maintenant, tapez les familles à déconstruire des applications sur des types arbitraires.
type family GetFun a where
GetFun (f _) = f
type family GetArg a where
GetArg (_ a) = a
Maintenant, une fonction de type très générale, plus générale que nécessaire pour répondre à la question. Mais cela permet une contrainte impliquant les deux composants de l'application.
type G (cfa :: (j -> k) -> j -> Constraint) (fa :: k)
= (fa ~ (GetFun fa :: j -> k) (GetArg fa :: j)
, cfa (GetFun fa) (GetArg fa))
Je n'aime pas offrir des fonctions de contrainte sans classes pour correspondre, alors voici une version de première classe de G
.
class G cfa fa => GC cfa fa
instance G cfa fa => GC cfa fa
Il est possible d'exprimer F
en utilisant G
et une classe auxiliaire:
class (cf f, ca a) => Q cf ca f a
instance (cf f, ca a) => Q cf ca f a
type F cf ca fa = G (Q cf ca) fa
class F cf ca fa => FC cf ca fa
instance F cf ca fa => FC cf ca fa
Voici quelques exemple utilise des F
:
t1 :: FC ((~) Maybe) Eq a => a -> a -> Bool
t1 = (==)
-- In this case, we deconstruct the type *twice*:
-- we separate `a` into `e y`, and then separate
-- `e` into `Either x`.
t2 :: FC (FC ((~) Either) Show) Show a => a -> String
t2 x = case x of Left p -> show p
Right p -> show p
t3 :: FC Applicative Eq a => a -> a -> GetFun a Bool
t3 x y = (==) <$> x <*> y
Il semble mieux si vous utilisez FC comme inflix opérateur. – 2426021684
'((~) Soit) \' FC \ 'Eq \' FC \ 'Eq' – 2426021684
@ 2426021684, bon point. – dfeuer