6

I have a tuple of values representing some state, and want to translate it by an addition (shift). My values are a longer version of ( Int, [Int], Int), and I want something conceptually (but not literally) like this:

shift n = ??? (+n) (id, map, id)     -- simple(?)

which would be equivalent to:

shift n (a, b, c) = (a+n, map (+n) b, c+n)

I am happy to just go with this explicit function usage, but wondered it there was a more idiomatic point-free version using Applicative or Arrows or ..., or if they would just end-up obfuscating things. I thought that the point-free version shows the basic structure of the operation more clearly.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
guthrie
  • 4,529
  • 4
  • 26
  • 31

3 Answers3

6

You can just write

shift n (a,b,c) = (a+n, map (+n) b, c+n)

Or define new combinators similar to Arrow's (***) and (&&&),

prod3 (f,g,h) (a,b,c) = (f a, g b, h c)     -- cf. (***)
call3 (f,g,h)  x      = (f x, g x, h x)     -- cf. (&&&)
ap3    f      (a,b,c) = (f a, f b, f c)
uncurry3  f   (a,b,c) =  f a    b    c

and then call

prod3 ( (+n), map (+n), (+n) ) (a,b,c)

or even (with appropriately set operator precedences)

ap3 ($ (+n)) (id, map, id) `prod3` (a,b,c)

Or, if you'd write your triples as nested pairs, you could use

Prelude Control.Arrow> ( (+5) *** map (+5) *** (+5) ) (1,([2,3],4))
(6,([7,8],9))

So for nested pairs,

shift' :: (Num b) => b -> (b, ([b], b)) -> (b, ([b], b))
shift' n = ( (+n) *** map (+n) *** (+n) )

(see also Multiple folds in one pass using generic tuple function)

Community
  • 1
  • 1
Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • Thanks; The nested pairs is a good idea and I tried it, but with longer state (four items) it is messy, and not really best for this situation. I was wondering about a generalization of ***, but it seems like not. – guthrie Sep 29 '14 at 13:30
  • When I had tried this approach: ( (+5) *** map (+5) *** (+5) ) I wondered if there is an easy way to reactor the common operations (+n) out of the definition (see the original question). – guthrie Sep 29 '14 at 13:32
  • well, `(f *** (f *** f)) = (***) f ( (***) f f ) = (***) f (join (***) f) = ( (***) <*> join (***) ) f`. For 4-tuples `((a,b),(c,d))`-shaped it'll be `(join (***) <*> join (***)) f`. – Will Ness Sep 29 '14 at 13:57
4

With the DeriveFunctor language extension you can write

data MyState a = MyState a [a] a
    deriving (Functor)

The derived instance looks like

instance Functor MyState where
    fmap f (MyState a bs c) = MyState (f a) (map f bs) (f c)

Now you can define

shift :: MyState Int -> MyState Int
shift n = fmap (+n)

(You say your tuple is even longer than (Int, [Int], Int), so you may want to define your state type using record syntax.)

Reid Barton
  • 14,951
  • 3
  • 39
  • 49
  • Thanks; I know I can do this and perhaps it is the "best" solution, but if there are no current operators for this so best to just write the function. I was thinking about something like a generalization of ***. – guthrie Sep 29 '14 at 13:27
1

Applying a list of functions to a list of values is simply

zipWith ($)

Since tuples of different sizes are each their own type, you will have to write a function explicitly for 3-tuples. A general function to apply a 3-tuple of functions to a 3-tuple of values is

apply (f, g, h) (a, b, c) = (f a, g b, h c)

You have to explicitly write each function application because tuples don't have the recursive property of lists.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268