B B
B B

Reputation: 97

A type for functions that pipe into each other

I want to deal with an abstraction where I can store f1, f2, f3, f4, f5... where

:t f5 . f4 . f3 . f2 . f1
-- String -> Int

I don't much care for the intermediary results, so I would like to do something like:

data PipeFunction a z = [(a->b), (b->c), ..., (y->z)] -- pseudocode

What kind of abstractions would allow me to do this? Right now the only thing I can think of is:

newtype PipeFunction a b c = PipeFunction (a->b, c->d)

but I would like it to be for arbitrary number of functions.

EDIT: A potentially illustrating example/use case, having an abstraction that allows me to perform this more elegantly: reactive app example

EDIT2: Follow-up. Is it possible to swap out elements of a type-aligned sequence? Working off of something like this:

swapPipe :: PipeFunction a z -> Int -> (b -> c) -> PipeFunction a z
swapPipe Nil _ _ = Nil
swapPipe (f :> fs) i g = f :> swapPipe fs i g
swapPipe (f :> fs) 0 g = g :> fs

Would dependent types in something like Idris be of use?

Upvotes: 1

Views: 158

Answers (1)

You can store them with a GADT, like this:

{-# LANGUAGE GADTs #-}

data PipeFunction a z where
  Nil :: PipeFunction a a
  (:>) :: Show b => (a -> b) -> PipeFunction b z -> PipeFunction a z

infixr 5 :>

And the example from your question:

import Data.Function
import Data.List

showPipe :: PipeFunction a z -> a -> [String]
showPipe Nil _ = []
showPipe (f :> fs) x = show y : showPipe fs y
  where y = f x

main = print $ showPipe (sort :> group :> sortBy (flip compare `on` length) :> head :> head :> Nil) [3,1,2,2,3,5,5,6,3,5,5,3]

-- Result: ["[1,2,2,3,3,3,3,5,5,5,5,6]","[[1],[2,2],[3,3,3,3],[5,5,5,5],[6]]","[[3,3,3,3],[5,5,5,5],[2,2],[1],[6]]","[3,3,3,3]","3"]

Upvotes: 5

Related Questions