37
$\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$ Commented Apr 23, 2013 at 3:16
  • 1
    $\begingroup$ @halirutan I knew that! :) $\endgroup$ Commented Apr 23, 2013 at 3:18
  • 3
    $\begingroup$ Related: Currying with Mathematica $\endgroup$ Commented 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$ Commented 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$ Commented 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$ Commented 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$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.