Is it possible to use function argument placeholders _ to flip their order?

Hi,

I am quite new to PureScript. Having explored its features, is is possible to use function argument placeholders _ to switch up the order, e.g. similiar to _ placeholders already used with Records?

Example:

myop1 :: String -> String -> String
myop1 s1 s2 = s1 <> s2

To fix second instead of first argument (via usual partial application) in myop1 I can do:

(_ `myop1` "foo" ) <$> ["a","b","c"] 
flip myop1 "foo" <$> ["a","b","c"]

, both resulting in ["afoo","bfoo","cfoo"].

Is is somehow possible to use the following?

(myop1 _ "foo" ) <$> ["a","b","c"]

More apparent with > 2 args, is there a more elegant way than this:

myop2 :: Int -> Int -> Int -> Int
myop2 x times minus = x * times - minus

(\n -> myop2 n 4 2) <$> [1,2,3] -- [2,6,10]

, such as

(myop2 _ 4 2) <$> [1,2,3]

?


Full test code:

module Main where

import Prelude

import Effect (Effect)
import Effect.Console (log)

main :: Effect Unit
main = do
  log ( (_ `myop1` "foo" ) <$> ["a","b","c"] # show  )
  log ( flip myop1 "foo" <$> ["a","b","c"] # show  )
  log ( (\n -> myop2 n 4 2) <$> [1,2,3] # show )

myop2 :: Int -> Int -> Int -> Int
myop2 x times minus = x * times - minus

myop1 :: String -> String -> String
myop1 s1 s2 = s1 <> s2
1 Like

_ as a function shorthand is only usable in fixed syntactic locations:

  • Case heads: case _, _ ,_ of
  • Records: { foo: _, bar: _ }
  • Record updates: _ { foo = _ }
  • if/then/else: if _ then _ else _
  • Operator sections: (_ <$> foo) or (foo <$> _) parens required, only one argument for a binary operation.

There is no section syntax for general function application. It was a deliberate design decision back in the day because the rules for the scope of a _ can get quite complicated as a general expression shorthand.

As an example:

_ { foo = f _ }

This could mean a couple of different things and you’d essentially have to pick a bias and require your users to internalize it. My impression is that there isn’t any motivation from the core team to try and resolve or extend this.

3 Likes

An alternative though might be the <@> operator which generalizes flip. (foo <@> "wat") <$> ... where <@> is like a placeholder.

3 Likes

Didn’t know <@>, thanks!

I also encountered mapFlipped / <#>, which is a map with swapped arguments.

May I ask, why flap generalizes flip? Looking at its source, I cannot see, where arguments get swapped:

flap (-) 3 4 == 1
(-) 3 4 == -1

, as opposed to flip which is clear from code.

The type of flip is:

forall a b c. (a -> b -> c) -> (b -> a -> c)

Desugar the arrows to Function:

forall a b c. (Function a (Function b c)) -> (Function b (Function a c))

Abstract Function a to f:

forall f b c. f (Function b c) -> (Function b (f c))

Resugar Function as ->:

forall f b c. f (b -> c) -> (b -> f c)

Rename b and c, to a and b respectively:

forall f a b. f (a -> b) -> (a -> f b)

Remove redundant parens:

forall f a b. f (a -> b) -> a -> f b

Add the necessary Functor constraint.

forall f a b. Functor f => f (a -> b) -> a -> f b

This is the signature of flap. That is, flip is flap instantiated to the Function x (for some x) Functor. Thus, flap generalizes flip, by applying the same rule to any functorial context.

5 Likes

Very elaborate, thank you so much @natefaubion !