web-dev-qa-db-ja.com

Haskell演算子と関数の優先順位

Haskellの演算子と関数の優先順位について自分で何かを確認しようとしています。たとえば、次のコード

list = map foo $ xs

次のように書き直すことができます

list = (map foo) $ (xs)

そして最終的には

list = map foo xs

私の質問は、なぜ最初の定式化が次のように書き直されないのかということでした。

list = (map foo $) xs

関数の優先順位は常に演算子の優先順位よりも高いので、私は答えを見つけたと思います。演算子を関数の引数にすることは許可されていません(もちろん、括弧で囲む場合を除く)。これは正しいですか?もしそうなら、RWHやLearn you a Haskell、または私が検索した他の場所にこのメカニズム/ルールについての言及がないのは奇妙だと思います。したがって、ルールが記載されている場所を知っている場合は、それにリンクしてください。

-編集:迅速な回答ありがとうございます。私の混乱は、演算子リテラルが何らかの形で何かに評価され、それが引数としての関数によって消費される可能性があると考えたことから生じたと思います。中置演算子は機械的に接頭関数に変換できることを思い出すのに役立ちました。これを最初の定式化に対して行うと、

($) (map foo) (xs)

ここで、($)が消費関数であり、2つの定式化が同等であるため、最初の定式化の$リテラルをマップで消費することはできません。

39
Boris

あなたは正しいです。このルールは、 Haskell Report で定義されているHaskell構文の一部です。特に、セクション3「式」では、関数適用(fexp)の引数はaexpでなければならないことに注意してください。 aexpでは、演算子をセクションの一部として、また括弧で囲まれた式内で使用できますが、裸の演算子は使用できません。

map foo $ xsでは、Haskell構文は、これが2項演算子$に適用される2つの式として解析されることを意味します。 sepp2kが指摘しているように、構文(map foo $)は左側のセクションであり、意味が異なります。

私はこれについてあまり考えたことがなく、実際にレポートでそれを調べて、オペレーターがなぜ彼らの行動をとるのかを確認しなければならなかったことを告白しなければなりません。

27
John L

まず、アプリケーション(空白)が最優先の「演算子」です。

第二に、Haskellでは、演算子と関数の区別は実際にはありません。ただし、演​​算子はデフォルトで中置されていますが、関数はそうではありません。関数をバッククォートで中置に変換できます

2 `f` x

演算子をparensの接頭辞に変換します。

(+) 2 3

だから、あなたの質問は少し混乱しています。

これで、特定の関数と演算子が優先順位を宣言します。これは、GHCiで「:info」を使用して見つけることができます。

Prelude> :info ($)
($) :: (a -> b) -> a -> b   -- Defined in GHC.Base

infixr 0 $

Prelude> :info (+)

class (Eq a, Show a) => Num a where
  (+) :: a -> a -> a

infixl 6 +

優先順位と結合性の両方を示します。

37
Don Stewart

すでに他の回答によって提供されている情報に加えて、異なる演算子は他の演算子に対して異なる優先順位を持ち、左/右または非連想である可能性があることに注意してください。 Prelude演算子のこれらのプロパティは、 Haskell 98レポートの固定性セクション にあります。

 + -------- + ---------------------- + ------------ ----------- + ------------------- + 
 | Prec- |左結合|非連想|右結合| 
 | edence |演算子|演算子|演算子| 
 + -------- + ---------------------- + ---------- ------------- + ------------------- + 
 | 9 | !! | | 。 | 
 | 8 | | | ^、^^、** | 
 | 7 | *、/、 `div`、| | | 
 | | `mod`、` rem`、 `quot` | | | 
 | 6 | +、-| | | 
 | 5 | | | :、++ | 
 | 4 | | ==、/ =、<、<=、>、> =、| | 
 | | | ʻelem`、 `notElem` | | 
 | 3 | | | && | 
 | 2 | | | || | 
 | 1 | >>、>> = | | | 
 | 0 | | | $、$!、 `seq` | 
 + -------- + ---------------------- + --- -------------------- + ------------------- + 

固定宣言がない演算子は、優先順位9に関連付けられたままであると見なされます。

関数適用の優先順位が最も高いことを忘れないでください(優先順位を考えてください10表の他の優先順位と比較) [1]

18
mucaho

違いは、中置演算子が引数の間に配置されるため、これ

list = map foo $ xs

プレフィックス形式で次のように書き換えることができます

list = ($) (map foo) xs

$演算子の定義によれば、これは単純です。

list = (map foo) xs
12

演算子を括弧で囲むと、関数の引数として渡すことができます(つまり、map foo ($) xs、実際には_(map foo ($)) xs_として渡されます)。ただし、角かっこで囲まない場合は、引数として渡す(または変数に割り当てる)ことができないのは正しいことです。

また、構文_(someValue $)_(_$_は任意の演算子)は実際には異なる意味を持っていることに注意してください。これは_\x -> someValue $ x_と同等です。つまり、演算子を左側のオペランドに部分的に適用します( _$_の場合はもちろんnoopです)。同様に、_($ x)_は演算子を部分的に右のオペランドに適用します。したがって、map ($ x) [f, g, h]は_[f x, g x, h x]_と評価されます。

10
sepp2k