Replace external functions/commands with JS

Bringing your stacks to the web

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller, robinmiller

alexchandel
Posts: 6
Joined: Thu Oct 11, 2018 8:16 pm

Replace external functions/commands with JS

Post by alexchandel » Thu Jan 12, 2023 8:18 pm

I have an external stack (DLL) that provides external functions (related to IO) to my stack. These functions cannot be implemented in LC script (they are far too large, and perform IO that cannot be done in LC), but I have a .js implementation that I can include in a script tag in the HTML file.

How do I invoke external javascript functions (like functions defined in a script tag) from LC?

Note that some of the external functions take and return large, complex objects, so serializing and string-escaping the objects into strings for some eval like

Code: Select all

do "foo(" && ... <string-escaped-monstrosity> && ")" as javascript
is out of the question. It's bad programming practice anyway, and doesn't result in execution in the browser's context to boot (confirmed with testing).

stam
Posts: 2682
Joined: Sun Jun 04, 2006 9:39 pm
Location: London, UK

Re: Replace external functions/commands with JS

Post by stam » Fri Jan 13, 2023 12:57 am

I know that some people simply load the JS libraries in a browser widget and call from there.
Fairly certain that it can work in both directions from what I've seen, but I have no expertise myself.
I can say that 'do as javascript' is the recommended way that I've seen, not sure why you consider this 'bad practice'?
If it doesn't work, it might simply be an issue with your code, rather than an inability to use a JS library...

I am however curious as to what is so 'monstrous' that LC cannot manage and JS can?
There isn't much that cannot be done more easily with LC... so genuinely curious.

alexchandel
Posts: 6
Joined: Thu Oct 11, 2018 8:16 pm

Re: Replace external functions/commands with JS

Post by alexchandel » Fri Jan 13, 2023 10:11 pm

I know that some people simply load the JS libraries in a browser widget and call from there.
The issue is calling/driving it from LCScript and passing data to/from it.
I can say that 'do as javascript' is the recommended way that I've seen, not sure why you consider this 'bad practice'?
Using "eval" for interop is bad programming practice, like passing user-generated format strings to printf or using Egyptian hieroglyphics in variable names. I'd link explanations but URLs are banned, so google "why is eval bad practice". These examples are in other languages but the reasoning is universal.
If it doesn't work, it might simply be an issue with your code, rather than an inability to use a JS library...
From the LiveCode documentation for "do":
In HTML5 applications, "JavaScript" is the only supported alternateLanguage. The result is set to the value returned by executing the statementList, as long as the value is a scalar (a string, number, boolean or undefined). Returning JavaScript arrays or objects is not supported.
That's a crippling limitation, not an issue with my code. First, this means a JS library with 100 non-OO functions must have each function wrapped with string-serialization, like

Code: Select all

function foo001(arg1)
    do "JSON->stringify(foo001(" & JsonExport(arg1) & "," & JsonExport(arg2) & "))" as "javascript"
    return JsonImport(result())
end foo001

function foo002(arg1, arg2)
    do "JSON->stringify(foo002(" & JsonExport(arg1) & "," & JsonExport(arg2) & "))" as "javascript"
    return JsonImport(result())
end foo002

function foo003(arg1, arg2, arg3)
    do "JSON->stringify(foo003(" & JsonExport(arg1) & "," & JsonExport(arg2) & "," & JsonExport(arg3) & "))" as "javascript"
    return JsonImport(result())
end foo003

etc... -- (note I can't type JSON-dot-stringify due to URL ban)
Second, functions that require instantiating objects and maintaining state between calls are impossible to wrap with LC, as JSON serialization destroys the object.

And every upstream change to the JS lib (which is updated frequently) requires manually fixing these bindings.
I am however curious as to what is so 'monstrous' that LC cannot manage and JS can?
There isn't much that cannot be done more easily with LC... so genuinely curious.
Take my previous example, or extend it: mapping these functions over data structures is painful in LC. In JS it was just:

Code: Select all

const exOutputs = exInputs->map(foo002)
But LC script has no functional or lambda syntax, and requires:

Code: Select all

local exOutputs
repeat with i = 1 to (number of elements of exInputs)
   put foo002(exInputs[i], i) into exOutputs[i]
end repeat
The more sophisticated the example, the greater the disparity will be.

stam
Posts: 2682
Joined: Sun Jun 04, 2006 9:39 pm
Location: London, UK

Re: Replace external functions/commands with JS

Post by stam » Fri Jan 13, 2023 11:45 pm

Well you may then want to wait for the next LC version which will have direct array assignment

jacque
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 7230
Joined: Sat Apr 08, 2006 8:31 pm
Location: Minneapolis MN
Contact:

Re: Replace external functions/commands with JS

Post by jacque » Sat Jan 14, 2023 7:46 pm

What version of LiveCode are you using? LC 10 dp 4 has added new syntax which may help. Open the release notes and search for "syntax" and you will see several new implementations that may help you avoid the repeat loop when creating arrays.

Excerpt of one example:
"Dictionary and sequence literal syntax has been added
allowing much easier expression of array-based values"

I don't understand what you need exactly but there are also several older functions that allow various conversions of arrays.
Jacqueline Landman Gay | jacque at hyperactivesw dot com
HyperActive Software | http://www.hyperactivesw.com

richmond62
Livecode Opensource Backer
Livecode Opensource Backer
Posts: 9375
Joined: Fri Feb 19, 2010 10:17 am
Location: Bulgaria

Re: Replace external functions/commands with JS

Post by richmond62 » Sat Jan 14, 2023 9:35 pm

URLs are banned . . .

Where?

By Whom?

SparkOut
Posts: 2852
Joined: Sun Sep 23, 2007 4:58 pm

Re: Replace external functions/commands with JS

Post by SparkOut » Sat Jan 14, 2023 10:13 pm

The forum software prevents users with fewer than "some number approximately 7" posts from including a url in the posting. This is to hinder spam accounts being created just to drop a link in and disappearing.
Once a user has posted a few genuine posts, this restriction is automatically removed.

richmond62
Livecode Opensource Backer
Livecode Opensource Backer
Posts: 9375
Joined: Fri Feb 19, 2010 10:17 am
Location: Bulgaria

Re: Replace external functions/commands with JS

Post by richmond62 » Sun Jan 15, 2023 10:23 am

Thank you for posting that information.

Is this stated explicitly on the page where new users sign up?

If not, it might be a good idea.

alexchandel
Posts: 6
Joined: Thu Oct 11, 2018 8:16 pm

Re: Replace external functions/commands with JS

Post by alexchandel » Wed Jan 18, 2023 12:03 am

stam wrote:
Fri Jan 13, 2023 11:45 pm
Well you may then want to wait for the next LC version which will have direct array assignment
jacque wrote:
Sat Jan 14, 2023 7:46 pm
What version of LiveCode are you using? LC 10 dp 4 has added new syntax which may help. Open the release notes and search for "syntax" and you will see several new implementations that may help you avoid the repeat loop when creating arrays.

Excerpt of one example:
"Dictionary and sequence literal syntax has been added
allowing much easier expression of array-based values"

I don't understand what you need exactly but there are also several older functions that allow various conversions of arrays.
I'm glad it has them now (20-30 years after Python and JS, 30-40 years after Lisp), but literals and direct array assignment don't cover mapping. In my example, I'm mapping the function foo002 over the list exInputs with enumeration. Mapping is a simple operation and a one-liner in every modern language, one of many that require multiple lines in LC Script.

But right now, I'm just trying to call a JS library from LC Script. The scalar-only limitation is serious, and forces me to JSON->stringify() everything (and JsonImport(the result)) and limits me to stateless calls (as state is maintained by objects or contexts, which don't survive stringification.

But it appears that async/await don't work in "do as javascript" calls, which error that "await is not a recognized variable", suggesting HTML5 deployment doesn't run them in an awaitable context for some reason (top-level await is supported in every browser + Node). So I'm at a loss for synchronizing asynchronous JS calls (the library makes many HTTP requests to do IO, and XMLHttpRequest()/fetch() and require callbacks or awaiting promises, while "do as javascript" is synchronous).

jacque
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 7230
Joined: Sat Apr 08, 2006 8:31 pm
Location: Minneapolis MN
Contact:

Re: Replace external functions/commands with JS

Post by jacque » Wed Jan 18, 2023 12:15 am

I'm out of my depth here but you can store anything you like in script local or global variables. I'd assume you can store the state of something that way. Someone who knows more about what you're trying to do may have an answer, I hope they'll chime in.
Jacqueline Landman Gay | jacque at hyperactivesw dot com
HyperActive Software | http://www.hyperactivesw.com

stam
Posts: 2682
Joined: Sun Jun 04, 2006 9:39 pm
Location: London, UK

Re: Replace external functions/commands with JS

Post by stam » Wed Jan 18, 2023 2:23 am

alexchandel wrote:
Wed Jan 18, 2023 12:03 am
I'm glad it has them now (20-30 years after Python and JS, 30-40 years after Lisp), but literals and direct array assignment don't cover mapping. In my example, I'm mapping the function foo002 over the list exInputs with enumeration. Mapping is a simple operation and a one-liner in every modern language, one of many that require multiple lines in LC Script.
While you mention having to use many lines for this in livecode, with the same token livecode does many other things with the fraction of the code required in other languages. Each language has its strengths and weaknesses (best to play to the former eh?). As far as I can tell, the essence of your complaint is that livecode isn't javascript... perhaps understandable given what you appear to be trying to do, but also something that is likely to improve in the near future given their roadmap.

Nevertheless, if you can't see a solution to your problem, you can always contact support at livecode dot com - they are very responsive and if it can't be solved easily you can submit an enhancement request - there is clearly a big drive for web at present, so chances are it's either solvable or there will be willingness to solve. If you get no further you can always submit a bug report.

The user forum is mainly an inter-user support tool, where if someone with expertise knows an answer you'll quickly get help. The corollary to this is that if no users know the answer, you won't (as is apparently the case here). So complaining about it in the user forums probably won't award the same degree of traction as just requesting official support from LC...

S.

stam
Posts: 2682
Joined: Sun Jun 04, 2006 9:39 pm
Location: London, UK

Re: Replace external functions/commands with JS

Post by stam » Wed Jan 18, 2023 3:14 am

alexchandel wrote:
Fri Jan 13, 2023 10:11 pm
I am however curious as to what is so 'monstrous' that LC cannot manage and JS can?
There isn't much that cannot be done more easily with LC... so genuinely curious.
Take my previous example, or extend it: mapping these functions over data structures is painful in LC. In JS it was just:
<snip>
You completely missed the point of my question.
I wasn't asking what javascript method you're trying to replicate in LC. I was asking what the overall intention of what you're doing is - because if you think LiveCode is a good fit for whatever it is you're doing, then maybe there is a better LiveCode way of doing it. If not, then perhaps LC is the wrong tool for the job. And by 'doing it', I don't mean a better way to run JS libraries, but the actual output of this library.

The essence of everything you have mentioned in this thread (as far as I can see) is that LC isn't Javascript - but that should not be a surprise. But just because LC <> JS doesn't necessarily mean it can't be done completely within LC script, or at least partially. Promises and await could theoretically be resolved by using script variables and "send in time" or some such. But you have given zero info about what's going on.

Again, what is it you're trying to do? You started this thread by stating
alexchandel wrote:
Thu Jan 12, 2023 8:18 pm
These functions cannot be implemented in LC script (they are far too large, and perform IO that cannot be done in LC), but I have a .js implementation that I can include in a script tag in the HTML file.
I'm curious what this "IO" is... and why this "IO" cannot be done in LC script?
If it really can't, can LC script not take over some of the state you're trying to maintain? It's all pure guesswork for anyone trying to answer.
Well, other than confirming that LC <> JS... and for many here that's a good thing.

jacque
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 7230
Joined: Sat Apr 08, 2006 8:31 pm
Location: Minneapolis MN
Contact:

Re: Replace external functions/commands with JS

Post by jacque » Wed Jan 18, 2023 5:48 pm

I'm curious what this "IO" is... and why this "IO" cannot be done in LC script?
I was wondering the same thing. There's usually a way to do almost anything in LC script.
Jacqueline Landman Gay | jacque at hyperactivesw dot com
HyperActive Software | http://www.hyperactivesw.com

jacque
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 7230
Joined: Sat Apr 08, 2006 8:31 pm
Location: Minneapolis MN
Contact:

Re: Replace external functions/commands with JS

Post by jacque » Wed Jan 18, 2023 9:47 pm

Since LC is my only programming language, that's how I think. But if I understand your "map" construct, you may be able to use the "union" command to do the same thing. Something like this:

Code: Select all

union exOutputs with foo002 recursively
Or maybe:

Code: Select all

union exOutputs with foo002 recursively into newArray
Jacqueline Landman Gay | jacque at hyperactivesw dot com
HyperActive Software | http://www.hyperactivesw.com

alexchandel
Posts: 6
Joined: Thu Oct 11, 2018 8:16 pm

Re: Replace external functions/commands with JS

Post by alexchandel » Fri Jan 20, 2023 4:40 am

stam wrote:
Wed Jan 18, 2023 3:14 am
The essence of everything you have mentioned in this thread (as far as I can see) is that LC isn't Javascript - but that should not be a surprise. But just because LC <> JS doesn't necessarily mean it can't be done completely within LC script, or at least partially.
Sure, LC doesn't have to be JS. JS (or TS) is very far from perfect, but it's packed with modern features and has millions of libraries, so the lack of feature parity and interop hurts. Turing-completeness means any pure computation (ie excluding IO, context changes, etc) can be done in either. One could program in Malboge and deploy to HTML5, but no one would ever want to.
jacque wrote:
Wed Jan 18, 2023 9:47 pm
Since LC is my only programming language, that's how I think. But if I understand your "map" construct, you may be able to use the "union" command to do the same thing. Something like this:

Code: Select all

union exOutputs with foo002 recursively
Or maybe:

Code: Select all

union exOutputs with foo002 recursively into newArray
Unfortunately not. Union combines two arrays. foo002 is a function, defined just above that example. Mapping a function f over an array [a, b, c, …, z] returns an array [f(a), f(b), f(c), …, f(z)]. I mapped with enumeration, which returns [f(a,1), f(b,2), f(c,3), …, f(z,26)]. These are two of the most common transformations in software/computer science.

Post Reply

Return to “HTML5”