関数適用
普及しているプログラミング言語の多くで、関数適用は関数名(引数)
という表記が用いられています。
PureScriptにおいて、関数適用は関数名 引数
という形で関数適用ができます。
例えば「整数を2倍する関数」としてdouble
関数があるとき、
double :: Int -> Int
double x = x * 2
double 10
という表記でdouble
関数に10
という値を適用できます。
> double 10
20
「10という値をdouble
関数で2倍して標準出力する」プログラムの場合は、
module Main where
import Prelude
import Effect (Effect)
import Effect.Console (log)
double :: Int -> Int
double x = x * 2
main :: Effect Unit
main = log (show (double 10))
となります。
main = log (show (double 10))
の部分が関数適用している箇所になります。
ここのカッコ(()
)は関数適用のためのものではなく、関数適用を優先させるために用いているものです。
このカッコが無い場合は「log
関数への引数としてshow
関数、double
関数、10
を与える」という意図しない意味になってしまい、型エラーとなるためです。
実際にカッコがない状態で以下のように書くと、
-- 型エラーになる書き方
main = log show double 10
「(log
関数の引数の型)String
にt0 -> String
の型は合わない」という以下のようなエラーになります。
Could not match type
t0 -> String
with type
String
while checking that type forall (@a :: Type). Show a => a -> String
is at least as general as type String
while checking that expression show
has type String
in value declaration main
where t0 is an unknown type
エラーの理由は、log
関数の引数としてshow
関数自体を渡そうとしてしまっているためです。
また、関数適用の方法として$
演算子を使うという方法があります。
$
演算子を使うと、先ほどのカッコを使って関数適用を優先させる必要が無くなります。
main = log $ show $ double $ 10
PureScriptはHaskellと同様に演算子は関数でもあり、演算子は2引数の関数適用の中置記法でしかないので、$
演算子の実態は$
関数であり、通常の関数適用が可能です。
PureScriptでは記号を用いた関数名を中置記法以外の方法で関数適用する場合は、その記号をカッコで囲う必要があります。
> -- ($)関数を普通の関数として関数適用してみる(通常はこのような書き方はしない)
> ($) double 10
20
そして、この$
関数はapply
という関数の別名(エイリアス)でもあるので、apply
関数を使った関数適用が可能です。
apply
関数はData.Function
モジュールで定義されており、apply
関数を直接使用するには明示的にData.Function
モジュールのapply
関数を読み込む必要があります。
> import Data.Function (apply)
> -- apply関数を使った関数適用(通常はこのような書き方はしない)
> apply double 10
20
また、記号ではない通常の関数であっても関数名を「`」(バッククオート)で囲うと中置記法で関数適用ができます。
> import Data.Function (apply)
> double `apply` 10
20
applyFlipped関数と#演算子
F#というプログラミング言語では、一般的な関数適用と書き順が逆になった引数 |> 関数名
という書き方で関数適用をする「パイプライン演算子」というものがあります。
PureScriptでも、このパイプライン演算子と同様に「引数のあとに関数名を書く」という表記で関数適用をするapplyFlipped
関数というのがあります。
🔗applyFlipped - Data.Function - purescript-prelude - Pursuit
applyFlipped :: forall a b. a -> (a -> b) -> b
Applies an argument to a function. This is primarily used as the (#) operator, which allows parentheses to be omitted in some cases, or as a natural way to apply a value to a chain of composed functions.
このapplyFlipped
関数は、関数適用させる関数の引数としての値と、関数適用させる関数の2つを引数として取ります。
通常の関数適用で書くと、以下のようになります。
applyFlipped 引数 関数
apply
関数と異なり「引数」「関数」という順番になっています。
先ほどのdouble
関数の例だと以下のようになります。
> applyFlipped 10 double
20
中置記法で書くと以下のようになります。
引数 `applyFlipped` 関数
double
関数の例だと以下のようになります。
> 10 `applyFlipped` double
20
また、applyFlipped
関数には別名として#
が定義されており、これを中置記法で書くと以下のようになります。
引数 # 関数
> 10 # double
20
「10という値をdouble関数で2倍して標準出力する」プログラムの例で、このapplyFlipped
関数としての#
演算子を用いると以下のようになります。
module Main where
import Prelude
import Effect (Effect)
import Effect.Console (log)
double :: Int -> Int
double x = x * 2
main :: Effect Unit
main = 10 # double # show # log
10 # double # show # log
の部分が#
演算子を用いた関数適用になっており、F#のパイプライン演算子を使った関数適用のような見た目になっています。
// F#の例
let double x = x * 2
10 |> double |> printfn "%d"
PureScriptで#
演算子を使うことで、データと関数適用の順番が左から右へと流れるような見た目となり、データの流れが見通しやすくなるかもしれないです。
しかし、一般的には通常の$
演算子が使われることの方が多いようです。