36
$\begingroup$

I'm trying to define some notation so that Mathematica code would be more functional, similar to Haskell (just for fun): currying, lambdas, infix operator to function conversion, etc.. And I have some questions about it:

  • Is it possible to make all Mathematica h_[x1_,x2_,...] functions to work as h[x1][x2][..]?
  • Can I distinguish inside Notation box between <+1> and <1+>, how do I check for + there?
  • How to define right-associate apply operator with highest precedence ($)?

This is what I have so far:

<< Notation`;

lapply[x_, y_] := x[y]
rapply[x_, y_] := x[y]
InfixNotation[ParsedBoxWrapper["\\"], lapply]
InfixNotation[ParsedBoxWrapper["$"], rapply]
x_ \ y_ \ z_ := x[y][z]
x_ $ y_ $ z_ := x[y[z]]

Notation[ParsedBoxWrapper[
RowBox[{
RowBox[{"λ", " ", "x__"}], "->", 
     "y_"}]] ⟺ ParsedBoxWrapper[
RowBox[{"Function", "[", 
RowBox[{
RowBox[{"{", "x__", "}"}], ",", "y_"}], "]"}]]]

Notation[ParsedBoxWrapper[
RowBox[{"〈", 
RowBox[{"op_", " ", "x_"}], 
     "〉"}]] ⟺ 
  ParsedBoxWrapper[
RowBox[{
RowBox[{"#", "op_", " ", "x_"}], "&"}]]]
AddInputAlias["f" -> ParsedBoxWrapper[
RowBox[{"〈", 
RowBox[{"\[Placeholder]", "\[Placeholder]"}], 
     "〉"}]]]

Notation[ParsedBoxWrapper[
RowBox[{"{", 
RowBox[{
RowBox[{"x_", " ", ".."}], " ", "y_"}], 
     "}"}]] ⟺ ParsedBoxWrapper[
RowBox[{"Range", "[", 
RowBox[{"x_", ",", "y_"}], "]"}]]]

filter[f_][x_List] := Select[x, f]
map[f_][x__List] := Map[f, x]

filter\PrimeQ $ map\〈-1〉 $ map\ \
(λ x -> 2^x)\ {1 .. 100}

EDIT: Also did some kinda lazy lists, soon it will be haskell inside Mathematica :)

SetAttributes[list, HoldAll]
list[h_, l_][x_] := list[h, l[x]]
list[x_] := list[x, list]

map[f_][list] := list
map[f_][list[x_, xs_]] := list[f[x], map[f][xs]]

take[0][_] := list
take[_][list] := list
take[n_Integer][list[x_, xs_]] := list[x, take[n - 1][xs]]

range[n_Integer] := range[1, n]
range[m_, n_] := list[m, range[m + 1, n]]
range[n_, n_] := list[n, list]

show[list] := "[]"
show[list[x_, l_]] := ToString[x] <> "," <> show[l]

show $ (take[10] $  map\ (λ x -> x^2) $ range[10000])
$\endgroup$
10
  • $\begingroup$ I don't know what you want to carry, but the guy's name was Haskell Brooks Curry and the technique of transforming functions with several arguments is call currying ;-) $\endgroup$
    – halirutan
    Apr 23, 2013 at 3:16
  • 1
    $\begingroup$ @halirutan I knew that! :) $\endgroup$
    – swish
    Apr 23, 2013 at 3:18
  • 3
    $\begingroup$ Related: Currying with Mathematica $\endgroup$
    – rm -rf
    Apr 23, 2013 at 3:52
  • 2
    $\begingroup$ swish, I haven't yet learned Haskell and as such I am not familiar with much of the syntax you aim to implement. It would be very helpful at least to me if you would describe in some detail the syntax/behavior that you desire. I chose not to attempt to debug your Notation Package code until I better understand what it is supposed to do. +1 for an interesting question, by the way. $\endgroup$
    – Mr.Wizard
    Apr 23, 2013 at 8:55
  • 2
    $\begingroup$ Re: lazy, see WReach's answer here. Also don't miss the implementation of that answer as a package by sblom $\endgroup$
    – rm -rf
    Apr 23, 2013 at 21:32

2 Answers 2

20
$\begingroup$

Currying

I don't know if it is possible to make all functions work in the Currying form (h[x1][x2][..]) but it is at least possible to extend Hold behavior to all arguments which natively that pattern will not have. I will copy my favorite method which I learned from this post by Grisha Kirilin:

SetAttributes[f, HoldAllComplete]

f[a_, b_, c_] := Hold[a, b, c]
f[a__] := Function[x, f[a, x], HoldAll]

Now:

f[2 + 2][8/4][7^0]
Hold[2 + 2, 8/4, 7^0]

I think conceivably most other Attribute behaviors could be implemented, but the pattern-matching changes of e.g. Orderless might be prohibitively difficult.

Formatting

Following jVincent's lead, if we would like to format the intermediate expressions cleanly we could use:

MakeBoxes[Function[x$, h_[a__, x$], HoldAll], _] := ToBoxes @ HoldForm[h[a]]

Now:

f[2 + 2][8/4]
f[2 + 2, 8/4]

This seems appropriate as f is already defined such that this form can be given as input. However, if one prefers the visual form f[2 + 2][8/4] then we can use:

MakeBoxes[Function[x$, h_[a__, x$], HoldAll], _] := 
  ToBoxes[HeadCompose @@ HoldForm /@ Unevaluated[{h, a}]]

f[2 + 2][8/4]
f[2 + 2][8/4]

(HeadCompose is a deprecated function but still quite useful.)
Note that in these rules I used x$ which is the automatic renaming of x that occurs within the Function. This code could be made more robust by using a more unique symbol name.

Low level Box forms

I cannot recall the limits of the Notation package in this regard, but you can determine how Mathematica parses an expression, and therefore what is accessible with $PreRead or CellEvaluationFunction, using a method from John Fultz (learned here):

parseString[s_String, prep : (True | False) : True] := 
  FrontEndExecute[UndocumentedTestFEParserPacket[s, prep]]

The default True should be used in our application as this is the form that will be seen by $PreRead, etc. Testing your example expressions:

parseString @ "<+1>"
parseString @ "<1+>"
{BoxData[RowBox[{"<", RowBox[{"+", "1"}], ">"}]], StandardForm}

{BoxData[RowBox[{"<", RowBox[{"1", "+"}], ">"}]], StandardForm}

One can see that in isolation these parse to distinct forms.

New operators

I described how to create a new operator in: How can one define an infix operator with an arbitrary unicode character?

And a more mild example in: Prefix operator with low precedence

I recommend that you do not attempt to redefine the $ symbol itself as your operator as this is extensively used internally in temporary symbol names (Module, Unique, etc.).

$\endgroup$
1
  • 1
    $\begingroup$ Very cool and simple "currying" definition. You can add something like MakeBoxes[Function[arg_, f[a__, arg_], HoldAll], StandardForm] := RowBox[{"f", "[", Sequence @@ Riffle[{ReleaseHold[ Function[x, ToString[Unevaluated[x]], HoldAll] /@ Hold[a]]}, "]["], "]"}] to make it still look simple as long as it's still gathering parameters. I suspect it could be achieved in a more elegant fashion, but it demonstrate the idea. $\endgroup$
    – jVincent
    Apr 23, 2013 at 12:33
11
$\begingroup$

That's how I finally defined haskell operators:

rapply[x_] := x
rapply[x_, y__] := x[rapply[y]]
InfixNotation[ParsedBoxWrapper["|"], rapply]

lapply[x_] := x
lapply[x__, y_] := lapply[x][y]
InfixNotation[ParsedBoxWrapper["∘"], lapply]

InfixNotation[ParsedBoxWrapper["·"], Composition]

Now $\circ$, $\dot{}{}$ and | act exactly like haskell's space, . and $ respectfully. Also if we have only single left application then @ is still helpful and it can be hidden with escape characters :@:.

And beautiful code like $\bf{show\cdot take\ 10\cdot map\ (\lambda\ x\to x{}^{\wedge}2)\cdot range | \infty }$ is possible. There are invisible @'s between take and 10, map and ($\lambda\ x\to x{}^{\wedge}2)$. In haskell the same would look like $\bf{show . take\ 10.map(\backslash x->x{}^{\wedge}2)$[1..]}$.

With double left application, $\circ$ is necessary: $\bf{map\circ(\lambda\ x\to x+1)\circ \{1,2,3\}}$

UPDATE: I made this TextCell hack to make partial infix operators:

infix[f_String] := Block[{x,y},Head[ToExpression["x" <> f <> "y"]]]

〈TextCell[s_][x_]〉 := infix[s][#, x] &
〈x_[TextCell[s_]]〉 := infix[s][x, #] &

We again can use invisible @ for application. Aliases can be made with TextCell on the left and on the right within AngleBrackets to enter them conveniently. Now stuff like <~Mod~10>, <2^>, <^3> also works.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.