scala - Extracting a constraint from a conjunction -


here's tree of boolean predicates.

data pred = leaf (a -> bool)             | , (pred a) (pred a)             | or (pred a) (pred a)             | not (pred a)  eval :: pred -> -> bool eval (leaf f) = f eval (l `and` r) = \x -> eval l x && eval r x eval (l `or` r) = \x -> eval l x || eval r x eval (not p) = not . eval p 

this implementation simple, problem predicates of different types don't compose. toy example blogging system:

data user = u {     isactive :: bool } data post = p {     ispublic :: bool }  userisactive :: pred user userisactive = leaf isactive  postispublic :: pred post postispublic = leaf ispublic  -- doesn't compile because , requires predicates on same type -- usercancomment = userisactive `and` postispublic 

you around defining data world = w user post, , exclusively using pred world. however, adding new entity system necessitates changing world; , smaller predicates don't require whole thing (postispublic doesn't need use user); client code that's in context without post lying around can't use pred world.


it works charm in scala, happily infer subtype constraints of composed traits unification:

sealed trait pred[-a] case class leaf[a](f : => boolean) extends pred[a] case class and[a](l : pred[a], r : pred[a]) extends pred[a] case class or[a](l : pred[a], r : pred[a]) extends pred[a] case class not[a](p : pred[a]) extends pred[a]  def eval[a](p : pred[a], x : a) : boolean = {   p match {     case leaf(f) => f(x)     case and(l, r) => eval(l, x) && eval(r, x)     case or(l, r) => eval(l, x) || eval(r, x)     case not(pred) => ! eval(pred, x)   } }  class user(val isactive : boolean) class post(val ispublic : boolean)  trait hasuser {   val user : user } trait haspost {   val post : post }  val userisactive = leaf[hasuser](x => x.user.isactive) val postispublic = leaf[haspost](x => x.post.ispublic) val usercancommentonpost = and(userisactive, postispublic)  // type inferred and[hasuser haspost] 

(this works because pred declared contravariant - anyway.) when need eval pred, can compose required traits anonymous subclass - new hasuser haspost { val user = new user(true); val post = new post(false); }


i figured translate haskell turning traits classes , parameterising pred type classes requires, rather concrete type operates on.

-- conjunction of partially-applied constraints -- (/\) :: (k -> constraint) -> (k -> constraint) -> (k -> constraint) type family (/\) c1 c2 :: constraint     (/\) c1 c2 = (c1 a, c2 a)  data pred c     leaf :: (forall a. c => -> bool) -> pred c     , :: pred c1 -> pred c2 -> pred (c1 /\ c2)     or :: pred c1 -> pred c2 -> pred (c1 /\ c2)     not :: pred c -> pred c  data user = u {     isactive :: bool } data post = p {     ispublic :: bool }  class hasuser     user :: -> user class haspost     post :: -> post  userisactive :: pred hasuser userisactive = leaf (isactive . user)  postispublic :: pred haspost postispublic = leaf (ispublic . post)  usercancomment = userisactive `and` postispublic -- ghci> :t usercancomment -- usercancomment :: pred (hasuser /\ haspost) 

the idea each time use leaf define requirement (such hasuser) on type of whole without specifying type directly. other constructors of tree bubble requirements upwards (using constraint conjuction /\), root of tree knows of requirements of leaves. then, when want eval predicate, can make type containing data predicate needs (or use tuples) , make instance of required classes.

however, can't figure out how write eval:

eval :: c => pred c -> -> bool eval (leaf f) = f eval (l `and` r) = \x -> eval l x && eval r x eval (l `or` r) = \x -> eval l x || eval r x eval (not p) = not . eval p 

it's and , or cases go wrong. ghc seems unwilling expand /\ in recursive calls:

could not deduce (c1 a) arising use of ‘eval’ context (c a)   bound type signature              eval :: (c a) => pred c -> -> bool   @ spec.hs:55:9-34 or (c ~ (c1 /\ c2))   bound pattern constructor              , :: forall (c1 :: * -> constraint) (c2 :: * -> constraint).                     pred c1 -> pred c2 -> pred (c1 /\ c2),            in equation ‘eval’   @ spec.hs:57:7-15 relevant bindings include   x :: (bound @ spec.hs:57:21)   l :: pred c1 (bound @ spec.hs:57:7)   eval :: pred c -> -> bool (bound @ spec.hs:56:1) in first argument of ‘(&&)’, namely ‘eval l x’ in expression: eval l x && eval r x in expression: \ x -> eval l x && eval r x 

ghc knows c a , c ~ (c1 /\ c2) (and therefore (c1 /\ c2) a) can't deduce c1 a, require expanding definition of /\. have feeling work if /\ type synonym, not family, haskell doesn't permit partial application of type synonyms (which required in definition of pred).


i attempted patch using constraints:

conjl :: (c1 /\ c2) :- c1 conjl = sub dict conjr :: (c1 /\ c2) :- c1 conjr = sub dict  eval :: c => pred c -> -> bool eval (leaf f) = f eval (l `and` r) = \x -> (eval l x \\ conjl) && (eval r x \\ conjr) eval (l `or` r) = \x -> (eval l x \\ conjl) || (eval r x \\ conjr) eval (not p) = not . eval p 

not only...

could not deduce (c3 a) arising use of ‘eval’ context (c a)   bound type signature              eval :: (c a) => pred c -> -> bool   @ spec.hs:57:9-34 or (c ~ (c3 /\ c4))   bound pattern constructor              , :: forall (c1 :: * -> constraint) (c2 :: * -> constraint).                     pred c1 -> pred c2 -> pred (c1 /\ c2),            in equation ‘eval’   @ spec.hs:59:7-15 or (c10 a0)   bound type expected context: (c10 a0) => bool   @ spec.hs:59:27-43 relevant bindings include   x :: (bound @ spec.hs:59:21)   l :: pred c3 (bound @ spec.hs:59:7)   eval :: pred c -> -> bool (bound @ spec.hs:58:1) in first argument of ‘(\\)’, namely ‘eval l x’ in first argument of ‘(&&)’, namely ‘(eval l x \\ conjl)’ in expression: (eval l x \\ conjl) && (eval r x \\ conjr) 

but also...

could not deduce (c10 a0, c20 a0) arising use of ‘\\’ context (c a)   bound type signature              eval :: (c a) => pred c -> -> bool   @ spec.hs:57:9-34 or (c ~ (c3 /\ c4))   bound pattern constructor              , :: forall (c1 :: * -> constraint) (c2 :: * -> constraint).                     pred c1 -> pred c2 -> pred (c1 /\ c2),            in equation ‘eval’   @ spec.hs:59:7-15 in first argument of ‘(&&)’, namely ‘(eval l x \\ conjl)’ in expression: (eval l x \\ conjl) && (eval r x \\ conjr) in expression:   \ x -> (eval l x \\ conjl) && (eval r x \\ conjr) 

it's more or less same story, except ghc seems unwilling unify variables brought in gadt required conjl. looks this time /\ in type of conjl has been expanded (c10 a0, c20 a0). (i think because /\ appears fully-applied in conjl, not in curried form in and.)

needless say, it's surprising me scala better haskell. how can fiddle body of eval until typechecks? can cajole ghc expanding /\? going wrong way? want possible?

the data constructors and :: pred c1 -> pred c2 -> pred (c1 /\ c2) , or :: ... not formed because type families cannot partially applied. however, ghc earlier 7.10 erroneously accept definition - give errors see when try it.

you should use class instead of type family; example

class (c1 a, c2 a) => (/\) (c1 :: k -> constraint) (c2 :: k -> constraint) (a :: k) instance (c1 a, c2 a) => (c1 /\ c2)  

and straightforward implementation of eval work.


Comments

Popular posts from this blog

facebook - android ACTION_SEND to share with specific application only -

python - Creating a new virtualenv gives a permissions error -

javascript - cocos2d-js draw circle not instantly -