Types of constraints: pass multiple constraints

When I have a data type such as haskell:

data A ctx = A (forall a. ctx a => a -> a) 

Then I can put functions that work with the values ​​of the types of this class into this data type:

 > A (+3) :: A Num A (+3) :: A Num :: A Num 

But is it also possible to put functions that have several limitations in this data type? I tried this:

 > :t ((+ 3) . succ) ((+ 3) . succ) :: (Enum c, Num c) => c -> c > :t A ((+ 3) . succ) :: A (Enum,Num) Expecting one more argument to `Enum' In an expression type signature: A (Enum, Num) In the expression: A ((+ 3) . succ) :: A (Enum, Num) 

So this will not work. How can I do what I want, or is it impossible? I know that one solution would be to use the "dummy" data type and type family, but I don't want to do this if there is another way because of its complexity. In addition, in this example, I could just use (+1) instead of succ, but there are more complicated cases when such a conversion to only one class is impossible.

+6
source share
1 answer

I found one way to generalize the idea of ​​combining several classes into one. Although it is rather inconvenient to use, it works.

 {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverlappingInstances #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} module Test where import GHC.Exts data (:>+<:) ctx1 ctx2 a where C'C :: (ctx1 a, ctx2 a) => (ctx1 :>+<: ctx2) a data (:++:) (ctx1 :: * -> Constraint) (ctx2 :: * -> Constraint) = C class Ctx2 c1 c2 a where useCtx :: c1 :++: c2 -> a -> ((c1 :>+<: c2) a -> b) -> b instance (c1 a, c2 a) => Ctx2 c1 c2 a where useCtx _ af = f C'C fC :: (Ctx2 Num Enum a) => a -> a fC a = useCtx (C :: Num :++: Enum) a $ \C'C -> 3 + succ a data A ctx = A { call :: forall a. ctx a => a -> a } main = print $ call (A fC :: A (Ctx2 Num Enum)) 3 

To determine Ctx3, Ctx4, ...

it should be possible to use restriction aliases.
+1
source

Source: https://habr.com/ru/post/947904/


All Articles