这是对我之前的问题的跟进。我在那里收到了一些很好的答案,但是,由于我简化了我的实际问题的方式,我认为我误导了回答者,我希望在这里解决这个问题。
TL;DR我有一个类型类Category
,constrained-categories
它Category
使用名为Object
. 我想做一个 Free Category
,但我很难证明表达式满足Object
约束。
考虑我的Free
数据类型及其constrained-categories.Category
实例:
data Free p a b where
Id :: Object p a => Free p a a
Comp :: Object p b => Free p b c -> Free p a b -> Free p a c
instance Category (Free p) where
type Object (Free p) a = Object p a -- passes through the constraints from instantiated `p`
id = Id
(.) = Comp
为了解释更多,让我们也考虑一个Category
我可能想要的非自由eval
:
newtype MyArr a b = MyArr (a -> b)
instance Category MyArr where
type Object MyArr a = () -- trivial, no constraints needed
id = MyArr P.id
MyArr g . MyArr f = MyArr (g . f)
我想写一个这样的表达式(这显然比我将在实践中编写的表达式简单得多):
-- error: Could not deduce: Object p Int arising from a use of ‘Id’
e0 :: Free p Int Int
e0 = Id
我可以用明显的方式解决这个问题,但是对于更大的表达式来说这会变得冗长。Object p _
想象一下组合元组的函数,并且需要为组合中的每个中间步骤显式地提供一个实例:
e1 :: Object p Int => Free p Int Int
e1 = Id
我可以选择不抽象Category p
, 并且,这行得通:
e2 :: Free MyArr Int Int
e2 = Id
但我想抽象一下。我应该认为添加一个约束Category p
应该起作用,并将其约束纳入范围type Object (Free p) a = Object p a
,并给我我需要的任何Object p _
实例,但是唉,它不起作用。
-- error: Could not deduce: Object p Int arising from a use of ‘Id’
e3 :: Category p => Free p Int Int
e3 = Id
在我看来,这QuantifiedConstraints
可能会有所帮助,例如forall a. Object (Free p) a => Object p a
,但你不能TypeFamily
在谓词的头部有 a 。
我也考虑过type Object p :: * -> Constraint
在 Conal Elliot 的concat库中使用 like,但是,这将需要不同的库、不同的Category
类,而且,上次我使用这种方式限制的类别时,它似乎有点麻烦,我什至没有确定这是否可以解决我的样板问题。该库中的一条评论QuantifiedConstraints
表明了 .
可运行
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
module ConstrainedCategoryFreeArrow3 where
import Prelude hiding (id, (.))
import qualified Prelude as P
import Control.Category.Constrained
-- * A Free 'Category'
data Free p a b where
Id :: Object p a => Free p a a
Comp :: Object p b => Free p b c -> Free p a b -> Free p a c
instance Category (Free p) where
type Object (Free p) a = Object p a -- passes through the constraints from instantiated `p`
id = Id
(.) = Comp
eval :: (Category p, Object p a, Object p b) => Free p a b -> p a b
eval Id = id
eval (Comp g f) = eval g . eval f
-- * A specific (trivial) 'Category'
newtype MyArr a b = MyArr (a -> b)
instance Category MyArr where
type Object MyArr a = () -- trivial, no constraints needed
id = MyArr P.id
MyArr g . MyArr f = MyArr (g . f)
-- * A generic expression
-- -- error: Could not deduce: Object p Int arising from a use of ‘Id’
-- e0 :: Free p Int Int
-- e0 = Id
-- works, but becomes verbose. Imagine for instance building an expression with
-- functions of tuples; you would need to provide an incredible amount of
-- boilerplate constraints for each permutation of types in tuples used anywhere
-- in the body. The problem is a little worse than this once you account for
-- `Cartesian` which has a Constraint Family called `PairObjects` where you need
-- to prove that each tuple is an allowed product in the current constrained
-- category.
e1 :: Object p Int => Free p Int Int
e1 = Id
-- works, but is no longer abstract in the category `p`
e2 :: Free MyArr Int Int
e2 = Id
-- -- ideal solution, but alas
-- -- error: Could not deduce: Object p Int arising from a use of ‘Id’
-- e3 :: Category p => Free p Int Int
-- e3 = Id