Deconstructing Arrows in Haskell

328 Views Asked by At

I am new to Haskell, and I have been playing around with Arrows. I would like to write a tool that can programmatically "disassemble" a previously constructed Arrow. As a potential application, imagine a function that takes in an Arrow and returns a directed graph that represents all the concatenations, splits, fan-outs, etc.

E.g., (f &&& g) >>> h yields something like

    ----- f ----
---|            |--- h -----
    ----- g ----

I initially thought I might be able to do this via pattern matching, as in the simple below (adapted from haskell.org Arrow tutorial), but it did not work.

module Main(main) where

import Control.Arrow
import Control.Category
import Prelude hiding (id, (.))

newtype SimpleFunc a b = SimpleFunc {runF :: (a -> b)}

instance Arrow SimpleFunc where
    arr f = SimpleFunc f
    first (SimpleFunc f) = SimpleFunc (mapFst f) where 
        mapFst g (a,b) = (g a, b)
    second (SimpleFunc f) = SimpleFunc (mapSnd f) where 
        mapSnd g (a,b) = (a, g b)

instance Category SimpleFunc where
    (SimpleFunc g) . (SimpleFunc f) = SimpleFunc (g . f)
    id = arr id


f,g :: SimpleFunc Int Int
f = arr (\x -> x - 5)
g = arr (\x -> 3*x + 1)

h1 :: SimpleFunc Int Int
h1 = f >>> g

h2 :: SimpleFunc Int (Int, Int)
h2 = f &&& g

# It would be great if I something like this worked
is_split :: SimpleFunc a b -> Bool
is_split (a1 >>> a2) = False
is_split (a1 &&& a2) = True
....

is_split h2 -- evaluates to True
is_split h1 -- evaluates to False

All my attempts to do this by defining my own types (i.e., a parameterized type that includes types of constituent children as well) have also failed.

Is there some way to "pull apart" the components of an arrow once it has been constructed?

2

There are 2 best solutions below

1
On BEST ANSWER

You can make a free arrow, which is like a tree, so you can inspect its structure. Or lower it to underlying arrow. One example is in the other SO question: Useful operations on free arrows

1
On

The answer is going to be no in general, because fanout and composition and the other arrow operators are functions, not constructors. You can "pull apart", say, a tree, because composing trees out of other trees preserves the constructors used to perform the composition, and then Haskell can pattern-match on them. But there's no guarantee that composing arrows will preserve the fanouts and compositions used to perform the composition. It's like adding 2+3 and then trying to pull apart the 5 later.