Currently, the Import node in AST contains only the path and selector of the target function. This is fine for JS and Python that have a similar import system.
In those languages, files are considered as module that contains exported functions, and a user of the module just has to add a directive like this to make use of this module function:
import modulefunction as localfunction from "module";
The module system provides an isolation to avoid name clashes when importing several modules.
This doesn't exist in Php. In php, there is a include/require/require_once directive that just "includes" the content in the scope:
require_once('module.php');
To avoid name clashes, Php has a namespace mechanism. The module.php file for instance can start with a namespace directive to scope all the definitions to the namespace:
namespace MyModule;
function myfunction() { return 0; }
A user of the module has to import it using require_once, but must then know the name of the namespace to use the functions:
require_once('module.php');
$x = \MyModule\myfunction();
To avoid repeating module prefixes, it is possible to define an alias:
require_once('module.php');
use \MyModule\myfunction;
$x = myfunction();
or even
require_once('module.php');
use \MyModule\myfunction as myfunc;
$x = myfunc();
But in both cases, knowning the namespace is mandatory.
Moreover, Php 7.2+ has a decent class construct, and it would be more idiomatic to compile class members as such. Fable today flattens all members as functions in the global scope. The Php transform could promote those functions as static or instance members. But the lack of information in Import make it impossible to generate the call:
module MyModule
type MyType(x: int) =
member this.Value = x
static member DefaultValue() = MyType 0
let increment x = x+1
Will generate imports to selectors like "MyType__Value" , "MyType__DefaultValue". But the call in php should look different:
$v = $instance->Value(); // or ->get_Value();
$d = \MyModule\MyType::DefaultValue();
$x = \MyModule\increment(1);
There is currently now way to make the distinction.
This can be observed when transpiling fable-library to Php
It seems this is also request for Python to support classes. @dbrattli
Yes, it would be great to be able to generate more idiomatic Python by having (static) members inside the class definition. I don't think it's a blocker for Python but would make it possible to generate nicer code.
I've found a function in Fsharp2Fable that determines if a class member should be transformed to a plain func or kept as a member. Modifying it for my purpose craeated other problems downstream, but overall it just simplified the transformation.
I had a call with @mathiasverraes this morning and I gave me some excellent insight about the php model. I started some refactoring today and managed to simplify greatly the conversion.
馃憤
@thinkbeforecoding Do you mean you don't need the extra information in the Import expressions now? Anyways, if it can help compilation to Python or other languages shouldn't be harmful to add it.
I'm painfully making tests pass one by one, 馃榿
And Php is far more touchy than JS..
For instance you can call a lambda in a $f variable with $f() and access a field $f in an object with $x->f, but if the field is a lambda, $x->f() will fail because this syntax is reserved for actual methods. You have to write ($x->f) ()...
To resolve this kind of 'subtleties' the transpiler need more info than in js.
I also need to know if parameters are optio al (for the inject stuff) because it has to be explicit in the declaration in php (with = Null for instance) and that info was not available in the parameter info structure.
For now I continue to explore, I add I do I need, and I'll review everything in the end to check that I did not abandon things on the way 馃憤
@thinkbeforecoding I just realized most of this information is in the call expression so adding it to the import would imply some duplication. Would it be ok for you to just use CallInfo or do you still need something else?
open Fable
open Fable.AST.Fable
match expr with
| Call(Import(importInfo,_,_), callInfo, t, r) ->
match callInfo.CallMemberInfo with
| Some memberInfo ->
memberInfo.DeclaringEntity,
memberInfo.FullName,
memberInfo.CompiledName,
memberInfo.IsInstance,
memberInfo.CurriedParameterGroups
I'll check if I can use it. Is import always in a call?
Import is not always in a call, but if it's called then it's in a call ;)
I've had a look at it, and a lot of calls have no CallMemberInfo (it is None).. so it's not enough for now.
Ah, it can be! Do you have any example? Maybe we are not adding the CallMemberInfo in the Replacements, I've to check 馃
I think I reach the limit for instance with calls to singleton on the AsyncBuilder.
The caller (in async test) try to emit somthing like:
singleton.Delay(...)
The AST is then:
```
Call(Get(Import { Selector = "singleton".. }, FieldGet("Delay", ..) ... )
````
The problem is that singleton is an import, so I have only the selector and filename. And the Call contains no CallMemberInfo (it is None, but it would be about Delay anyway)...
So there is no way to know in what namespace singleton is without adding more info in Import...
I managed to reduce the quantity of information necessary in Import info.
Fable generates Import for different things:
So for now, it seems enough to add the namespace to ImportInfo, and setting the type for Import of types.
@thinkbeforecoding I've added back the ImportKind (which was actually there in Fable 2) to the Import expression. For internal imports, this will include the file full path, which you can use to retrieve the namespace with com.GetRootModule as you already do. I've also included information about whether it is an instance or static member or a class, though I should warn you that trying to attach instance members may take work. Actually the F# compiler already compiles instance members as module functions and to change that you need to change how the arguments are passed, the name mangling, etc.
Ah yes, I think that should help. I'll try to work a bit on this next week 馃憤
@alfonsogarciacaro , is it in a specific branch ?
@thinkbeforecoding Sorry, I hadn't merged it yet 馃槄 It's now in the next branch: https://github.com/fable-compiler/Fable/blob/38dd35e70d4e290048474e5c924a99fc38c4f5ad/src/Fable.AST/Fable.fs#L297-L301