jkrumbiegel / Chain.jl

A Julia package for piping a value through a series of transformation expressions using a more convenient syntax than Julia's native piping functionality.
MIT License
368 stars 16 forks source link

Approach for nesting a map #45

Open max-sixty opened 2 years ago

max-sixty commented 2 years ago

Related to https://github.com/jkrumbiegel/Chain.jl/issues/42#issuecomment-988614614 & https://github.com/jkrumbiegel/Chain.jl/issues/44

Is there a recommended approach for nesting a map? This would allow arbitrary nesting in a single point-free expression — something that's really difficult in languages I've explored.

For example, if we wanted to use @chain for the whole expression, it would currently look something like:

@chain "a|x\nb| y z " begin
    strip
    split("\n")
    map(_) do x
        @chain x begin
            split("|")
            map(_) do y
                @chain y strip split
            end
        end
    end
end

But the x and y aren't needed here! Assuming there isn't an existing recommended approach — could we extent Chain to handle those too, with feature for reducing map(_) do x; @chain x begin down to something like map @chain do, so we could have:

@chain "a|x\nb| y z " begin
    strip
    split("\n")
    map @chain do
        split("|")
        map @chain do 
            strip
            split
        end
    end
end

I'm not sure of the exact form that's both concise and idiomatic — I don't have a view on the exact syntax. And feel free to say this is a bad idea... Thanks again.

jkrumbiegel commented 2 years ago

I was thinking along similar lines, I've been testing out things in this branch https://github.com/jkrumbiegel/Chain.jl/tree/function-chain

Your proposed syntax doesn't work, you can always test such things by prefacing the code with Meta.@dump which will give you the syntax tree (if it's parseable, which this is not) even if nothing in it corresponds to real variables and functions.

The do syntax needs a call-like object before it (with parentheses) so I thought about this:

@chain "a|x\nb| y z " begin
    strip
    split("\n")
    @chain map() do
        split("|")
        @chain map() do 
            strip
            split
        end
    end
end

This would then work for arbitrary functions like filter, etc, that take a closure as the first argument which itself expects one argument.

I even thought about transforming any do syntax without a closure argument, even without @chain in front, but that's probably a step too far.

max-sixty commented 2 years ago

That looks really good. I'd be more than happy to test it over the next few days.

I even thought about transforming any do syntax without a closure argument, even without @chain in front, but that's probably a step too far.

Your sense is obv better than mine — but I don't think it'd be possible to have ambiguity, given that map() do \n isn't otherwise valid. Someone could write map() do x \n if they wanted the standard do syntax. (But OTOH I guess easier to expand than retract later...)

jkrumbiegel commented 2 years ago

Yes for map that's true, but I'd want it to work for any function that takes a closure, and some don't have any args and that would probably be a really confusing edge case if such functions started to error.