I've (kinda) implemented `$` operator in Nix

with builtins; let
    simpl = e: if isS e then resolve e.stack else e;
    
    isS = s: s.type or "" == "$";
    
    resolve = stack:
        assert stack != [];
        let ss = map simpl stack;
        in foldl' (f: a: a f) (head ss) (tail ss);

    export = {
        S = {
            type = "$";
            stack = [];
            __toString = self: "$${self.stack}";
            __functor = self: arg: (
                if arg == s:e 
                then resolve self.stack 
                else self // {
                    stack =
                        if self.stack == [] then [arg] else
                        if isS arg then [arg] ++ self.stack else
                        [(head self.stack arg)] ++ tail self.stack;
                });
        };
    };
in 
# tests
with export;
    assert S map (a: a + 1) S map (a: a * 2) S map (a: a - 2) [1 2 3 4] s:e == [ (-1) 1 3 5 ];
    assert S map (a: a * 2) S [1 2 3 4] s:e == [ 2 4 6 8 ];
# /tests
export

S is an equivalent of $, and s:e unwraps S functor to value, and can be thought as a closing bracket)

Implementation is quite trivial, but I hope this example will encourage you to try implementing your own interesting custom stack executors for Nix, and making language more rich)

Please, share ideas in the comments!

6 Likes

You should have mentioned what the $ operator actually is. Not everyone knows Haskell.

This is the comment from GHC.Base on $ (Rendered)

-- | Application operator.  This operator is redundant, since ordinary
-- application @(f x)@ means the same as @(f '$' x)@. However, '$' has
-- low, right-associative binding precedence, so it sometimes allows
-- parentheses to be omitted; for example:
--
-- > f $ g $ h x  =  f (g (h x))
--
-- It is also useful in higher-order situations, such as @'map' ('$' 0) xs@,
-- or @'Data.List.zipWith' ('$') fs xs@.
--
-- Note that @('$')@ is levity-polymorphic in its result type, so that
-- @foo '$' True@ where @foo :: Bool -> Int#@ is well-typed.
7 Likes

I didn’t even know that was possible, amazing! Could you add that to Nixpkgs’ lib?

I wish we could use the actual $ symbol here though.

Related is my GitHub - Infinisil/nixlisp :smiley:
But I’d very much discourage using this for anything other than a cool demo. It’s neat, but very hacky! Certainly shouldn’t be added to nixpkgs.

5 Likes

I am not sure that’s good enough for nixpkgs lib, but I could make a draft)

Wow! Reflection through parsing source file from unsafeGetAttrPos is one of the coolest hackiest things I’ve seen in Nix!