compiling-to-categories / concat

Compiling to Categories
http://conal.net/papers/compiling-to-categories
BSD 3-Clause "New" or "Revised" License
431 stars 49 forks source link

Metaprogramming/escaping ccc? #11

Open oliver-batchelor opened 7 years ago

oliver-batchelor commented 7 years ago

Is it possible to manipulate the ccc expression using normal functions as I'm trying to do below?

For simple things it seems it can work because you can inline them, something like the foldCat I've given below won't work because foldr1 won't be inlined and then ccc does it's work and replaces the arrows.

{-# INLINE foldCat #-}
foldCat :: (Ok3 k a b (Prod k a b),  ConstCat k a, ClosedCat k) => [a] -> Prod k a b `k` b -> b `k` b
foldCat xs f = foldr1 (.) $ map (flip applyConst f) xs

{-# INLINE applyConst #-}
applyConst :: (Ok4 k a b c (Prod k a b), ConstCat k a, ClosedCat k) => a -> Prod k a b `k` c -> b `k` c
applyConst a f = f . (const a &&& id)

main :: IO ()
main = print $ render (ccc program)
   where program xs = foldCat xs addC
conal commented 7 years ago

Hi @Saulzar. I expect that you'd either run into an inlining failure (and consequent ccc residual) or an infinite inlining loop. I don' think the use of categorical vocabulary is helping other than a bit of reduction to the work done by the ccc plugin.

Is it possible to manipulate the ccc expression using normal functions as I'm trying to do below?

I'm not sure that I understand what you're asking, since Haskell code cannot manipulate Haskell expressions without the programmer having to use a meta trick such as Template Haskell, haskell-src, or a compiler plugin. It is certainly possible to manipulate values denoted by expressions built out of categorical vocabulary, as with other Haskell values.

oliver-batchelor commented 7 years ago

I guess what I'm trying to achieve is to be able to take an expression built with ccc and manipulate it with regular Haskell then embed that expression as part of another expression built with ccc.

I see I can actually do this using a custom operation which works - though slightly clunky. (Edited a little for clarity, Op2 is your AltCat macro with another argument to specify the renamed function rather than having another module 'C'). I also realize a class is not necessary here.

type FoldC k a b = (Ok3 k a b (Prod k a b),  ConstCat k a, ClosedCat k) =>  [a] -> (Prod k a b `k` b) -> (b `k` b)
foldC :: FoldC k a b
foldC as f = foldr1 (.) $ map (applyConst f) as

Op2(fold, foldC, FoldC k a b)
oliver-batchelor commented 7 years ago

A related idea, I've been trying to embed an scalar expression inside an array/tensor expression for map/zip/reduce type operations in order to do automatic differentiation. I didn't manage to get anything like this to work properly...

instance MapCat (->) where
  type Scalar (->) = (->)
  mapC f = fmap f

instance MapCat Syn.Syn  where
  type Scalar (Syn.Syn) = Syn.Syn
  mapC f = Syn.app0 ("Map " ++ Syn.render f)

type MapC k = forall a. (Scalar k a a) -> (Tensor a `k` Tensor a)
class MapCat k where
  type Scalar k :: * -> * -> *
  mapC :: MapC k

Op1(map, mapC, MapCat k => MapC k)

Which gives me this kind of error..

      In the expression: map
                           @ Syn
                           $fMapCatSyn
                           @ Int
                           ((\ (ds_Xg3q :: Int) ->
                               case ds_Xg3q of _ [Occ=Dead] { I# x_ag3W -> I# (+# x_ag3W 1#) })
                            `cast` (Sub (Sym D:R:Scalar(->)[0]) <Int>_N <Int>_N
                                    :: ((Int -> Int) :: *) ~R# (Scalar (->) Int Int :: *)))
      Argument value doesn't match argument type:
      Fun type: Scalar Syn Int Int -> Syn (Tensor Int) (Tensor Int)
      Arg type: Scalar (->) Int Int

I also tried adding a helper function like this, without much change... just the error expression message becomes much larger.

map' :: MapCat k => (a -> a) -> (Tensor a `k` Tensor a)
map' f = map (ccc f)
{-# INLINE map' #-}