Oh my… Exactly, one year ago I wrote some article about interop with Fable. That’s some subject I begin to be comfortable with. At least I can grasp some concepts. And I want to share this with you. Again. Because interop has been my main course for so many years…
But first, a word of warning…
This is a highly personal text which holds some highly hazardous technical facts mixed with personal experiences. I am not so sure this all blends well. In fact I think I’m the most terrible technical writer you can find on Medium. So you may lose a lot of time reading me. Especially because I’m French and I like the French way of telling stories: no facts, only feelings. I would also add that, you will have to tolerate my more than dubious English. After all everybody knows that French people can’t write proper English.
You’ve been warned!
What is interoperability?
If we take the official definition from the AFUL, the French speaking Libre Software Users’ Association, “Interoperability is a characteristic of a product or system, whose interfaces are completely understood, to work with other products or systems, present or future, in either implementation or access, without any restrictions.”
So, for years, I have been doing just that: making systems work, talk and interface with other systems. But before I begin, one must know that, at first, it was not really my choice…
Once upon a time…
Most of the time we had to create some messaging interface to be able to communicate through all these layers. And when we did that in 2003, it would just be some kind of torture.
Please, don’t misunderstand me. At that time I would enjoy this kind of pain, creating some generic system which would allow communication through xml messages. As a Java lover I would spend day reflecting upon the best way to name my methods within a 50 characters limit and try to apply that to ActionScript.
Later when we really went berserk about ActionScript optimizations we discovered long names would take so much memory! Painful memories: our tech lead would then use x,y,z everywhere. And nobody wanted to make peer reviews: “hmm, what does this xy var hold?”.
ActionScript 1.0 was dynamic like hell. The true land of Mutation.
ActionScript 1.0 was dynamic like hell. The land of Mutation. ActionScript 2.0 added to this dynamism oriented object programming. I was so happy! now I could work with UML diagrams! The world held so much promises…
Our goose is cooked!
Yes it didn’t take long to see that we would end in a wall.
Maybe not all of us. We had in house specialists who liked their highly mutable prototypes. After all, it allowed them to add fields when they felt they needed some. For instance I remember a time when I saw a new score field popping from nowhere at the end of the code of a game just because we didn’t need it before…Or a field that would mutate from int to string to whatever.
Isn’t it a great proof of high mutability powers?
But you know what comes next: nasty bugs found through trials and errors. It literally meant we had to really play to our games to find them.
No. Wait. I never wrote that. I never even meant it. It just popped into this highly mutable piece of text!
So one day, our R&D lead decided to unleash his OCaml powers to make things right. And it later became the Haxe language. Strictly Typed and oriented object. Still mutable but much less.
So we switched our server code base to Haxe and did the same for our clients.
Interop: a long quest.
After years doing Haxe code I started enjoying writing bindings or wrapper or glue code or whatever the name you give to create interfaces to make two pieces of code work together.
Then I did the same with Scala and now I do the same with Fable.
So my very first real Fable project was about interop: bindings for the Pixi.js library.
Pixi samples for Fable. Contribute to fable-compiler/samples-pixi development by creating an account on GitHub.
Why Pixi? Because at this time I was using it for my freelance projects. So I switched all my Haxe projects to Fable 0.7.
You may wonder why? Because after doing some Scala I had this crush for functional and Fable came out of nowhere in a most elegant synchronicity.
So my first paid project with F# and Fable pushed me into interop. It was a crash test. But. In fact. It never crashed.
That’s when I understood that both the language and the tools were was I have been looking for years. A light yet elegant and powerful and functional typed language with not so many keywords to remember… What a blessing for my weak mind!
And great interop features right from the start. So I could not be afraid, it was like feeling at home.
Notwithstanding, of course, the awesome Fable community. But here, if you know me, you know I will never cease to praise the men and women who are sharing their Fable experiences with mere mortals like me.
Interop with Fable.
So let’s get back to the root of our subject, shall we? The first question I’ll raise is: how one learns Interop?
First of all I would advise to have a look to the greatest resources out there:
- The de facto interop bible by Zaid Ajaj. This is were I learnt so many things.
- ts2fable which allows you to transpile Typescript definitions to F#. It’s often the tool of choice. It comes in two flavors: as a cli and as an online gui.
These I already wrote about in my previous articles:
- Fable for busy moms and dads: use a JS lib in one minute!
- Fable for busy moms and dads: jsPDF in your next project in a few minutes
Foreign interfaces or the Royale path…
Thanks to the hardworking done for the ts2fable project, the Royale Path allows you to take the luxury of downloading some Typescript definitions for the JS library you want to use and then, transform them to nice F# files.
Let’s take an example. Let’s say I’d like to use the bcrypt library to do some nice encryption stuff. Because, usually in the morning, It’s what I’m doing while waiting for my tea to brew.
- download the bcrypt package: yarn add bcrypt
- download the typescript definitions: yarn add @types/bcrypt
- Transform them to nice F#: ts2fable path/to/nodemodules/@types/bcrypt/index.d.ts Fable.Import.Bcrypt.fs
Here’s the result:
Not so bad… Let’s see… What’s inside exactly? A very simple interface IExports.
Now the next question is: how can I use this interface? What’s the magic to make this communicate with the actual bcrypt library?
Here we go:
We just add this to the beggining of our file. It says: “I will import bindings which are exposed by the bcrypt library through the use of my IExports interface.”
In other words it will map the exposed methods of the bcrypt library to my IExports interface.
So here’s the final result:
Next question: how do I use this in my fable project?
- Open my bindings
- Use my library
(note the promise is just there because I’m using a promise)
Like I said in my last talk at FableConf, I can read into minds. So I know that next question is: “But François, let’s say, for the sake of the argument that I haven’t mapped all the methods in my interface. Will it still work?”
Sometimes if you don’t need to use everything then don’t map everything.
Of course John. Of course Melinda. Of course Pierre Paul Jacques. That’s what I want to come to right now: sometimes if you don’t need to use everything then don’t map everything.
With my Bcrypt example, it seems simple but let’s take another library: nodemailer.
NodeMailer is a very nice project to send mail from your nodejs app. More than nice, in fact it is the library you want to use to send mails from your nodejs app. It’s featured quite everywhere to the point it’s part of the IBM Cloud functions standard packages.
So it’s packed with so many nice features that it conveniently sorts them in most logical files: JSONTransport, SESTransport, SMTPTransport, etc…
Well in my case, let’s say I want to send my mails through my SMTP server. Do I need all the extra features the library is offering? No.
Do I want to take all the nice definitions file you see on the left of this wonderfully written piece of text and use ts2fable to create some huge definition file that may not work at all because as good as it is, ts2fable can sometimes misinterpret things? Nope.
I could also just transform only the required definitions. For instance smtp-connection.d.ts and shared.d.ts. But then I will find that I’m missing some other definitions. So I will transform that too. But then it will require something else. So in the end I will spend days just to be able to send a simple mail! No. I don’t want to do that. Never. (again…)
Send e-mails! Easy as cake! That’s the motto of nodemailer. That should be my line of conduct too!
So I have a life that needs me to do things not connected with computers. I have kids I want to play with. So I don’t want to do that. I don’t want to spend days writing the most the most complete definition files in order to send a single mail. So I don’t want to use the Royale Path. I want to use the…
So what do I really need? How can I know? Well I should start with the documentation. Let’s see. I want to send a mail using SMTP. Are there some samples? Yes.
So it seems it all starts with a transporter:
At first glance I can see that the createTransport function needs some options and defaults. It also says that I should read below.
Ok so now I see. I t would be nice to have some sample though. Let’s see if I can get one…
Ok. So it seems I’ve got all I need to create what they call a transporter. Now how can I actually send a mail?
Ok. Now I’ve got everything. It seems I can pass a callback to a sendMail function. Ok so not so many things to do after all… And maybe I could use some promises?
Great! It’s time to build something from all this information:
- First we need some Interface to bind the library to. Let’s call this INodeMail interface.
- The createTransport function needs some config object. Let’s create that. We’ll call this Config. The Config auth field seems to need a user and pass fields. Let’s add that and create an Auth record.
- It seems the createTransport function returns a Transport object so Let’s create an ITransport interface.
- The transporter.sendMail function sends back either and error or an Information objects as a result. Let’s wrap this too.
- Finally, let’s bind this to the lib through and inport directive and…
That’s it! A proper library to use nodemailer. How long did it take? Well roughly one hour. Most of the time because I had to read the docs.
Now let’s use this!
Et voilà !
Now I know you’ve got a last question. Because you read the sample above and you are wondering why I use this cryptic code to check the response from the sendMail function.
It’s because, our sendMail function can either send an Info object or an Error. But as these are interfaces, Fable won’t be able to determine what I really get in my output…
So how do we know if we get an Error or an Info object? Well simply by testing if a field exists!
The Info object holds a very convenient messageId field. Let’s just see if it’s defined with the following instruction:
The magical ? operator is there to allow you to get access to whatever field you’d need. Per the doc:
So you see, sometimes we do love a little bit of dynamism… And obviously sometimes we do require that just not to get crazy!
A most informal conclusion
when one does not need to use a whole JS library, it’s often faster to do things manually
So the main idea I tried to develop in this article was: when one does not need to use a whole JS library, it’s often faster to do things manually. It’s really not that hard. It just requires to read the doc. Honestly, nothing very complicated here.
That’s it for this little article about interop. I hope I didn’t make you lose a lot of time. I hope you accepted the Frenchness of my writings. I know it can be rude. But now I’m an old French guy. I need my baguette and my French wine ;)
A last serious word: don’t hesitate to ask me questions. I will try to answer them all.
Merry Christmas! See you next year!