Adding dollar sign (apply) to nix

3 minutes read

This post is very low quality for comedic effect.

A detailed list of instructions

Take one nix, apply this patch[1]:

What does this do? It adds a new token $ to the nix languages parser and exposes it as __dollar, essentially by copying the way this works for __lessThan.

Hack on your nix:

nix develop
./bootstrap.sh
./configure $configureFlags --prefix=$(pwd)/outputs/out
make -j $NIX_BUILD_CORES
outputs/out/bin/nix repl

Now in the nix repl that just opened:

nix-repl> __dollar = x: y: builtins.foldl' (a: b: a b) x y

This makes $ an infix operator that’s essentially just a generic apply.

Now just create a toy example to demonstrate:

nix-repl> concat = x: y: z: (x + y + z)
nix-repl> concat $ ["literally" " " "dollar sign"]

And your repl will say:

"literally dollar sign"

Tadaa! That’s all it takes to add the $ operator to nix, just like Haskell.

The end $\text{\(^ヮ^)/}$


  1. diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
    index 201370b90..4b26af623 100644
    --- a/src/libexpr/parser.y
    +++ b/src/libexpr/parser.y
    @@ -353,6 +353,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
     %left AND
     %nonassoc EQ NEQ
     %nonassoc '<' '>' LEQ GEQ
    +%nonassoc '$' DOL
     %right UPDATE
     %left NOT
     %left '+' '-'
    @@ -411,6 +412,8 @@ expr_op
       | expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$3, $1})); }
       | expr_op '>' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$3, $1}); }
       | expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$1, $3})); }
    +  | expr_op '$' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__dollar")), {$1, $3}); }
    +  | expr_op DOL expr_op { $$ = new ExprOpNot(new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__dollar")), {$3, $1})); }
       | expr_op AND expr_op { $$ = new ExprOpAnd(makeCurPos(@2, data), $1, $3); }
       | expr_op OR expr_op { $$ = new ExprOpOr(makeCurPos(@2, data), $1, $3); }
       | expr_op IMPL expr_op { $$ = new ExprOpImpl(makeCurPos(@2, data), $1, $3); }
    diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
    index ddf529b9e..00622a142 100644
    --- a/src/libexpr/primops.cc
    +++ b/src/libexpr/primops.cc
    @@ -3583,6 +3583,24 @@ static RegisterPrimOp primop_lessThan({
         .fun = prim_lessThan,
     });
    
    +static void prim_dollar(EvalState & state, const PosIdx pos, Value * * args, Value & v)
    +{
    +    state.forceValue(*args[0], pos);
    +    state.forceValue(*args[1], pos);
    +    // pos is exact here, no need for a message.
    +    CompareValues comp(state, noPos, "");
    +    v.mkBool(comp(args[0], args[1]));
    +}
    +
    +static RegisterPrimOp primop_dollar({
    +    .name = "__dollar",
    +    .args = {"e1", "e2"},
    +    .doc = R"(
    +      Just like haskell :)
    +    )",
    +    .fun = prim_dollar,
    +});
    +
    
     /*************************************************************
      * String manipulation