mentat-collective / emmy

The Emmy Computer Algebra System.
https://emmy.mentat.org
GNU General Public License v3.0
394 stars 22 forks source link

Handle structural args in sicmutils.expression.compile/compile-fn #78

Open sritchie opened 3 years ago

sritchie commented 3 years ago

Right now, compile-fn (link) can handle functions of different arities, and the GUTS of compile-state-fn can handle arguments with any structure.

It would be great to unify these, so compile-state-fn called the guts of compile-fn, and the latter could take an arbitrary argument template instead of just n for its arity.

Some nice design work waiting here!

littleredcomputer commented 1 year ago

At this point compile-fn and compile-state-fn are built on the same foundation, so this is easy. All that is required is for the customer to supply a dummy argument list where each argument has the correct shape. Then fmap gensym over the dummy argument list and evaluate. We can preserve the existing behavior of inferring arity if no dummy argument is supplied.

sritchie commented 1 year ago

I'm looking forward to unifying this with the "literal function descriptor language", so the customer could pass a function signature built out of those pieces.

sritchie commented 1 year ago

@littleredcomputer wdyt, should we call this done? I do sense that there is a bit more unification we could do.

littleredcomputer commented 1 year ago

One last unification we could do would be to remove the arity argument from compile-fn: instead, force everyone to provide an application model. Maybe that seems lame for plain functions. Essentially force everyone to call compile-state-fn as it exists now, and then rename it to compile-fn. how about that?

Here's the only difference left:

([f n opts]
   (let [argv (into [] (repeatedly n #(gensym 'x)))]
     (compile-state-fn f false argv (merge {:calling-convention :native
                                            :arity n}
                                           opts))))

If we made the default calling convention :native (currently it is :structure), then we're done. You have to specify the model, which can often be inferred, but oh well. From the point of view of ODE, the initial-state will serve in this role. Can we say that the only meaningful calling conventions left are :primitive and :native? I can see the use case for both: mathbox can make use of :native for state->xyz, and if we are going to ODE, then :primitive is a refinement of :native.

Looking back at this, I think we should take the further step. The only difference between :native and :primitive, from the point of view of the generated arglist, is whether the state model should be thought of as one thing or several. We could insist that the state model have the same length as the function desired. That would force callers to wrap the state model in [] in the SICM case