0

I have this code which I'd like to condense

runFn someFn $ toArray a1 $ toArray a2 $ toArray a3 $ toArray a4

I would envision something like

runFn someFn <$> fmap toArray [a1, a2, a3, a4]

In that case runFn someFn would create a partially applied function that awaits its missing parameters and gets then applied one by one on the elements of the array.

I must admit I dont know if the type system will allow for this.

Edit As it was asked for - here the actual type signatures for this. However my question is a more general one:

Can I put the parameters of an function into an array if those parameters are of the same type and the partially apply the function array element by array element.

type Numbers = Array Number
foreign import _intersect :: Numbers -> Numbers -> Numbers -> Numbers -> Point2D
data Point2D = Point2D Number Number
toArray :: Point2D -> Numbers
toArray (Point2D x y) = [x, y]

a1 = Point2D 0.5 7.0
a2 = Point2D 3.0 5.1
b1 = Point2D 2.5 9.0
b2 = Point2D 2.1 3.6

intersection :: Numbers -- 2 element Array Number
intersection = _intersect (toArray a1) (toArray a2) (toArray b1) (toArray b2)
robkuz
  • 9,488
  • 5
  • 29
  • 50

2 Answers2

2

If I understand what you're asking here, no you can't. You'd need dependant types to be able to express this, as you'd need some way of ensuring the function arity and array length are equal at the type level.

gb.
  • 4,629
  • 1
  • 20
  • 19
1

If we are allowed to change the question slightly, we might be able to achieve what I think you want. The fact that all functions are curried allows us to use type class instance resolution to come up with some tricks that sort of allow us to do things with functions of arbitrary arity. For example:

module Main where

import Prelude
import Data.Foldable (sum)
import Control.Monad.Eff.Console (print)

class Convert a b where
  convert :: a -> b

data Point2D = Point2D Number Number
type Numbers = Array Number

instance convertId :: Convert a a where
  convert = id

instance convertPoint :: Convert Point2D (Array Number) where
  convert (Point2D x y) = [x, y]

instance convertChain :: (Convert b a, Convert r r') => Convert (a -> r) (b -> r') where
  convert a2r b = convert (a2r (convert b))

f :: Numbers -> Numbers -> Number
f xs ys = sum xs * sum ys

g :: Point2D -> Point2D -> Number
g = convert f

f' :: Numbers -> Numbers -> Numbers -> Numbers -> Number
f' a b c d = f a b + f c d

g' :: Point2D -> Point2D -> Point2D -> Point2D -> Number
g' = convert f'

main =
  print $ g'
    (Point2D 1.0 2.0)
    (Point2D 3.0 4.0)
    (Point2D 5.0 6.0)
    (Point2D 7.0 8.0)

These techniques have some nice applications. QuickCheck, for example, uses a similar technique to allow you to call quickCheck on functions of any arity, as long as all the arguments have Arbitrary instances. In this specific case, though, I think I would stick to the simpler, more boilerplate-y solution: unrestrained use of type classes can become quite unwieldy, and can produce very confusing error messages.

hdgarrood
  • 2,141
  • 16
  • 23