関数適用
普及しているプログラミング言語の多くで、関数適用は関数名(引数)という表記が用いられています。
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で#演算子を使うことで、データと関数適用の順番が左から右へと流れるような見た目となり、データの流れが見通しやすくなるかもしれないです。
しかし、一般的には通常の$演算子が使われることの方が多いようです。