Integrated Dynamics

Integrated Dynamics

63M Downloads

Flip doesn't unwrap operator at end of signature

narwhalfire opened this issue · 6 comments

commented

Issue type:

  • 🐛 Bug

Short description:

I had trouble picking a title that didn't basically say, "flip works as expected". As far as the type system exposed by ID is concerned, flip works exactly how it's specified to work. However it does not work as expected if all functions are to be treated as they are curried. The problem here seems to be less on flip, and more on operators having a fixed number of arguments instead of being curried. Flip just happens to be bad at dealing with this since it is naive in assuming arity.

Steps to reproduce the problem:

ID's apply operator can sort of be recreated by applying pipe to id. The difference being that the last two terms are packaged in an operator type.

apply :: (a -> b) -> a -> b
synth_apply :: (a -> b) -> (a -> b)

These operators are equivalent if they are curried, but not under ID. Apply can be flipped while synth_apply cannot. That example seems to describe every case I tested.

I agree with @JHawkley's statement in #751

The flip operator probably needs to perform a check to see if an arity-1 operator that it received returns another operator when applied. The flip operator should still be able to execute, in that case.

Perhaps a nice feature would be that anywhere the signature would show an operator, it instead shows the signature of that operator in parenthesis. Or maybe just expand the operator at the end of the signature when the operator is created?


Versions:

  • This mod: 1.12.2-1.0.17
  • Minecraft: 1.12.2
  • Forge: 14.23.5.2838
commented

@narwhalfire Could you give a (small) concrete example of a scenario where this becomes clear?

commented

A scenario where the error with flip is clear or a scenario where the proposed solution is clear?

commented

The former, a sequence of (applied) operators where the flip error is observable. Together with a brief explanation of what you would expect to happen instead.

commented
  1. Get cards for operators pipe, id, and apply
  2. ‘apply pipe id’ to get card named synth_apply
  3. ‘flip synth_apply’, materializing this results in error: “The operator Flip received the operator Applied Operator with input length 1 while length 2 is required.
  4. ‘flip apply’, materializing this works with signature ‘Any -> Op -> Any’

I’d expect that step 3 would behave the same way as step 4 since apply and synth_apply have equivalent type signatures. More accurately, they are expected to have the same type signatures, but under ID, apply has two inputs and synth_apply has one input.
In Haskell, ‘apply pipe id’ has the same signature as ‘apply’.

commented

I'm going to give this issue a lower priority.
The actual usage of synth_apply seems to work without problems.
Unless currying is done over it, but in that case the regular apply should be used.

The proper fix for this issue would be to make operator I/O type calculation more dynamic, to support more complex edge-cases like these. I currently don't see a proper way of handling this. Perhaps I (or someone else) may come up with a proper solution for this in the future.

commented

Fixing this would also make it possible to build an operator that rotates inputs like #636.
For Func :: A -> B -> C -> D this should be possible by making an operator pipe(pipe(Func, flip), flip). However, pipe(Func, flip) gives you A -> operator (which should technically beA -> C -> B -> D)and that can't be flipped currently.