-
Notifications
You must be signed in to change notification settings - Fork 32
Stop using auto-generated files #75
Comments
I think there is several things in your comment :)
Main problem here is that we are mapping a dynamic language where everything is allowed to a strongly typed and opinated language. For example, declaration order matter in F# where not in JavaScript. Using recursive module is the solution to solve order declaration problem and even with that we can't support all the JavaScript eco-system.
Then, we should improve ts2fable to include them.
Here I think it highlights severals things:
I saw a lot of people creating bindings by replacing the type (class/interface) with module/function. So basically they were doing a one to one mapping to the JavaScript and couldn't port everyting.
I tend to think that people makes fixes for themselve because it's easier. For example, several bindings in this repo don't contains information that they were auto generated and even so they didn't receive much more love. Perhaps, people don't use them it's also a possibility. I think, that people tend to not send fixes, because it needs several steps:
Yes and no. We can't say from the time the binding has been generated once, we should never generate it again. In general, we generate the binding, add improvement by hand to them. Like converting
If you think it can help, sure we can remove these commands from the library in this repo. :) In general, bindings in this repo are first generated and improved over time by hand as I described it. Which is in fact what you said :) |
Yes, TypeScript and JS does not necessarily map well to F#, which is why auto-generating the bindings might not be the right thing to do.
Most modules could probably be written without being recursive. Now it looks like every generated module is recursive. It confuses users and it confuses Ionide. The same goes for with
Yes, but as you say, the JS ecosystem is so complex so ts2fable will always be 10 steps behind. Getting users to send fixes for bindings might be hard, but require them to submit fixes for ts2fable is much harder.
Yes, what happens now is that you give up on the bindings and write the few functions you need yourself.
If you need to regenerate the binding because of massive updates (e.g version 0 to version 1), then version 0 should first be put in a v0 directory before generating a new one (e.g using ts2fable).
I think it would help a lot to remove the comment, add a README.md for usage information for each library, and encourage developers to contribute and improve the bindings when they find missing pieces, and also contribute to the usage information. That is the only way to move forward and improve the experience of using Fable beyond the SAFE template and Fulma. Ts2fable is a great tool, but I think we are expecting too much, and using it the wrong way. |
Before answering, I just want to say thank you for taking time to discuss on this issue. And I want to say, I am not against anything you say. I am discussing and explaining things so we can find a common ground and perhaps result in a list of action to do :)
This is indeed a good idea but the migration process I mentioned is still valid otherwise you would have potential regressions.
I guess we can agree on that point. Auto generation is probably hard or impossible to support but we need to use it to do 90% of the job.
I don't know, I remember times where we didn't have the recursive module and to be honest it was a lot of trial and error to fix the order declaration. About Ionide going crazy, if we can avoid it that's nice but that an F# feature so it should support it :) From my experience, the problem is more that there was a lot of line to parse than the recursives modules. For example, you can try editing Fulma library and documentation site in Ionide and you will see it freeze, etc. As a maintainer the recursive modules simplify the process of maintaining a library because files between 2 generation from ts2fable looks the same and so I can apply the correct change and still keep the manaul improvements made. That's something impossible if you re-order the file manually. If you don't re-order the declaration in the exact same order then the diff will not help you and you will probably miss porting manual improvement made.
Well, if people does that then they are just fixing their today problem and not the one from tomorrow. I mean when I am porting a library I always port all of it because I never know what I will need and I don't want to write binding all the day. I prefer to do it once and be done with it :) But this point is probably out of the subject here. It's just a side effect of all the others point you mention :)
Well people tend to like auto/magic stuff and will always. I know that as people love auto decoders when I "hate" them :) Perhaps, this is up to us to educate people and explain how ts2fable is meant to be used. Have good explanation of the issues, limitations, etc. |
Sorry, I haven't read the full thread yet but you're right @dbrattli. The dream of having mostly automatic bindings with very little maintenance is going away with each new release of Typescript as the type system diverges more a more from F#. Giving up automatic translation with ts2fable (or better, restricting it to the first draft) goes also in line with the discussion in #73 to split the browser bindings in different Web APIs. At the end we'll probably have to deprecate this, have each binding in its own repo and ask for volunteers to maintain them when possible (moving the repos if necessary) as we've done with Fable.PowerPack. Then, we just need a page in Fable's website that allows user to search for available packages/bindings, as discussed in fable-compiler/fable-compiler.github.io#32 |
Ok, @alfonsogarciacaro that sounds like a great idea! It might still be good to have the separate repos under fable-compiler but delegate the maintenance to Collaborators with write access to the given repo only. That way it's easier for someone else to take over in case the repo gets abandoned. That's better than having external repos that may easier die out and other will only have the option to fork to continue. I'm going to work on Leaflet, ReactLeaflet and GeoJSON in the months ahead, so if you split those into separate repos I would be happy to contribute. |
I duplicated this in #78 because I didn't search for the right keywords. Closing that issue and re-posting here with some modifications. I completely agree with @dbrattli. Auto-generated bindings, such as the Electron bindings, are not particularly F# idiomatic and can cause high friction. This goes for type safety as well as other stuff (e.g. avoiding explicit callbacks in favour of A couple of concrete examples I came across:
Fable does not hide the fact that it compiles to JS. This is both a great strength (it's fairly easy to interact with unsupported JS APIs, and devs have to learn a bit about the JS runtime environment anyway) and a significant weakness (non-trivial code needing several JS APIs, even supported ones through bindings such as in this project, may end up looking in part like JS code written with a weird and limiting syntax, and can cause troubles when functions can return e.g. I'm therefore of the clear opinion that API/bindings in general should be more F# idiomatic. The bindings should not only allow calling the JS API in the first place; the JS API should (where performance allows) be wrapped and presented in a form that causes low friction from F#. I might be able to assist with this, but I'd need clear guidelines on what kinds of things are desirable and acceptable to change, and it would likely progress slowly due to my limited capacity in the foreseeable future. I can also make no promises as to how committed I can be to cleaning up everything (as opposed to just a few bits here and there to scratch my own itches), or whether I will be able to continually maintain an F# idiomatic API as new bindings are needed for new JS API surfaces. Note though that I think there are a lot of API changes that can be done and benefited from in isolation, so there is no strict need to do this "all at once" (except for the fact that all changes would likely be breaking, and it might be desirable to update as much as possible at once to avoid many major releases). |
The thing is that in order to make the API feel more .Net you would have to write a lot of it by hand. And check the documentation in order to see all the possible case and then create the correct bindings. For point 1, we use For the point 2, we are binding Electron API. If you want to use promise it's the same as in JavaScript you have to write a helper. And we could publish it in the package just like we include react helpers with the bindings in For point 3, if we generated For point 4, we kept the same name as in JavaScript so in general, the F# code is the same as in JavaScript and people can then benefit from JavaScript documentation, SO answers, etc. For me, point 1, 2, 3 are the same. As with all the bindings, we generated the code using ts2fable and then it needs tweaking by hand. I mean, imagine having the If you want and if others are ok, you can indeed propose a PR to the electron binding in order to make it more friendly. |
Yes, that's exactly the point. Given the current state of auto-generated bindings, I think hand-crafted bindings could considerably improve the usability of the bindings and make Fable even more attractive and easy to use. It would also make it possible to include function/parameter documentation in cases where the TS bindings don't include them (it seems to be missing in a lot of places). It does leads to significantly more maintenance, though.
Is that a bad thing? In any case: When creating hand-crafted bindings, how to I know whether to prefer
Sorry, I didn't understand this. Aren't docs alone sufficient to create bindings? (Assuming the docs cover the complete API.) |
Yes, docs are sufficient or even the source code of the library. I did it for Fulma for example and it took me 3-4 months of work to port the whole API. And Fulma was "simple" because it was just CSS wrapper. In what you propose, when porting the API it's not only making the bindings but also all the helpers for the functions that are using callbacks. But in this case, should we make the helpers ourself or use an existing JavaScript library who is already converting to callback and so make the bindings for it and so on etc. But I can't imagine someone crazy enough doing it an API like electron or worst VSCode. But I would be really happy to be proven wrong 😉. I am kind of playing a bit the advocate of devil here to help identify the pain point. And be able as a community to weight the pros and cons and also consider the amount of work to be done. |
I have the same experience unfortunately. No easy solution really, the state of community maintained bindings will be a function of how active the community is - so I guess we just keep telling people how great it is! I've tried to use ts2fable on a few libraries and have had some very limited success stripping out just the types that I need. Generally though, the whole thing falls apart when I start trying to change things and I'm not knowledgeable enough with JS or F# to know how to fix anything. And I can't use the file itself because Ionide grinds to a halt. So my usual experience is: get a working javascript example up and running in about a minute - then try to port it and spend 5 hours hacking away with types and end up with a spaghetti of red squiggles, then give up. I'm sure it would be an incredible time saving tool for a more experienced developer though. For those of us who are doing this to try and avoid JS, we end up getting stubborn and really wanting types rather than falling back to dynamic ha. I think if I felt more comfortable with interop generally this probably wouldn't be such a pain point. At the moment I feel poring through other (non generated) bindings (and resources like this whitetigle article on medium), learning how they do things, then applying the knowledge to just pluck the types I need out is probably the way forward. |
Hello @drk-mtr , thank you for sharing your experience. Writing a binding indeed isn't an easy task. And need the developer to learn how JavaScript data transpose to F# types. Did you found this interop guide. It's a great learning resource and almost complete. Would you say we are missing documentation and examples on how to write bindings? How to structure it? I did write several bindings in the past weeks and was wondering if sharing my experience and how I did it as @whitetigle did would help or no. |
@MangelMaxime, I think sharing your experience would definitely help because today the main point is: how can we convince people to use fable if they can't use their favorite libs? We hear here and there that Fable is awesome. I'm the first to say that because really it just saved me tons of hours of debugging and refactoring. But yes, interop is not easy. It's not strictly a Fable topic though: whatever the language, interop is not easy, has never been and will never be. ts2Fable can help get started but also seem to block things too because it's never really precise to the point we can use it right away (ints being floats, obj when all else fails). Especially when like PouchDB you have code splitted into several .ts files. Like @MangelMaxime wrote, recursive modules have made things easier in most cases. Before that it was just crazy to find the proper order. But that's not enough. That will never be. So we need to do some refactoring and then it appears as something painful because of a common psychological bias: we thought it would do all the hard work for us! So I see things like this: ts2fable allows one to discover an API then starts the real editing work. ts2fable is very important and very powerful in this process. It really helps. But that should be its only goal: help us get started. So I think either we find a way to interop seamlessly with js/typescript or we accept the compensation of writing bindings like artists ;) Now, what we can do is share more if not support everything. I mean, I do have many bindings like everybody here but I don't want to share them that much because they're fitted to my purposes and do not map 100% of the libraries features and I won't really support them, add/deprecate features unless I need them. And it's usually used for 5 lines of code in a project. But maybe we could start an interop network of gists for instance, and directly ping one another if we're stuck or need more information. Maybe before we add a proper section in the next version of Fable web site we could start by linking things in awesome-fable? Meanwhile let's continue/start to share our interop experiences. What do you think? |
pinging @Zaid-Ajaj would help I think ;) |
@MangelMaxime Yes I think anything that anyone can do to share experiences would help, I find these posts really informative. Considering the level of contributions the people on this thread have made, I find it hard to comprehend and quite inspiring that you manage to find the time in your day to be honest. Immensely thankful for the software and any documentation is a bonus. So I feel the onus is on people like me to document their experiences while learning (when I get to the point where I'll be assisting more than misleading!). I've seen Zaid's guide, it's becoming my bible hehe. To be fair, my attempts have generally been with monolithic libraries of the babylon / three variety, not little libraries and therefore a big ask. One thing I'm curious about - when Ionide struggles with the large recursive file - I'm assuming there would be no way to tell it to do less checking, since that would mean it can't extract types for use elsewhere? Not sure whether it's worth asking Krzysztof as I don't know whether it's Ionide or on the compiler side and therefore out of his hands... |
This is true that Ionide is having a hard time when you have a large recursive file or if you got a lot of files in your project. For example, when working on Fulma Ionide works fine 5-10min and after that, it can't keep up. However, when you have the bindings ready/working. If you create a nuget package then Ionide will use the I think a way to check if this is a bug/limitation of Ionide you can test working with VisualStudio if your are on Windows and see if the problem stays. |
I'll give that a go - thanks. |
Working with the current bindings is not a good experience for developers. The ts2fable generated code is hard to read (recursive modules), missing imports, and generally a pain to use making developers spend hours and hours struggling before giving up or re-implement the binding manually themselves. This is hurting both the Fable community, and the idea of embracing the JS community. No one wants to patch auto-generated files so developers makes fixes for themselves that doesn't help others. Using ts2fable to generate the initial bindings is ok, but from then on it should be maintained manually (imo). So comments like
// ts2fable 0.5.2
should be removed so developers get encouraged to work on them manually and improve them.The text was updated successfully, but these errors were encountered: