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!

10 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.
9 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!

1 Like