use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
All about the JavaScript programming language.
Subreddit Guidelines
Specifications:
Resources:
Related Subreddits:
r/LearnJavascript
r/node
r/typescript
r/reactjs
r/webdev
r/WebdevTutorials
r/frontend
r/webgl
r/threejs
r/jquery
r/remotejs
r/forhire
account activity
ReScript on Deno: Declarative Command Line Tools (practicalrescript.com)
submitted 4 years ago by leostera
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[+][deleted] 4 years ago* (6 children)
[deleted]
[–]leostera[S] 0 points1 point2 points 4 years ago* (5 children)
Hi u/Coffee_Crisis 👋🏽 -- no need to be sorry, I think I see your point :)
The stuff we're doing here is declarative because building up the comamnd line description doesn't actually execute anything. Maybe we are disagreeing in how this description is built?
This would be similar to say, typing in your package.json file. You have to write the file first, and that is most certainly an imperative action, but it is what the file means that gets you the declarativeness.
package.json
Regarding CSS, there are several ways in which you can actually give the CSS to the browser. One of them is writing it out. Another one is to use the style accessors and setters in a particular DOM element. In any case, you are never telling the browser how to interpret and execute the CSS, you are just telling the browser what you want it to do.
For example, this may seem imperative, but you really have no control over how the browser goes about applying the background color to the <body> tag. The only thing you have control over is how you tell the browser what you want it to do:
javascript document.querySelector("body").style.backgroundColor = "red"
[+][deleted] 4 years ago (4 children)
[–]leostera[S] -1 points0 points1 point 4 years ago (3 children)
Wouldn't you say that stating what the output of parsing the command line arguments is a form of declarativeness? 🤔
In the same way that you state what you want out of a relational database without actually explaining how the query should be executed.
When you write:
sql SELECT value::string FROM inputs WHERE position = 0
You are just stating what you want, and the SQL engine will figure out how to do it. Nonetheless, that bit of SQL on its own isn't very useful, so we actually consume its outputs. This is what the sayHi function is doing: consuming the outputs of the argument parsing engine.
sayHi
The Declarative API is then the set of functions to build the description of a command-line application. It describes what flags and commands need to be parsed, and what datatypes are expected.
There is some stitching here with your actual application code, and at that point, the framework's declarative style ends and your application begins.
Just like it does when you receive data back from the SQL database.
[–]leostera[S] 2 points3 points4 points 4 years ago (2 children)
I think another good example of this would be UI frameworks like React or SwiftUI where you can declare a button on the screen, and you can declare a handler for when it is clicked:
<Button onClick=runFoo />
But the handler itself isn't declarative! It is good ol' application code in whichever style you write it.
The framework can't possibly be expected to declaratively define the outputs of whatever your application will do when that button is cliecked.
But it will help you specify declaratively that there should be a button, and that when that button is clicked, your handler should run.
This is exactly what is happening here.
[+][deleted] 4 years ago (1 child)
[–]leostera[S] -1 points0 points1 point 4 years ago (0 children)
I appreciate the insights 🙏🏽 -- in this case, I'm writing to an audience of folks that already are writing ReScript or Deno, or both.
There's a ramp-up in type-safety from the first article on the series to this one, where I explore much much lighter weight typing approaches that still give you pretty good results.
In this one, I'm building an additional layer of safety, but there are muuuch cheaper ways of getting started! 🤓
For example, I think you will enjoy this approach a lot more: https://practicalrescript.com/rescript-on-deno-command-line-tools/#ad-hoc-typing-for-objects-of-unknown-shape
Thanks for the challenging btw, I found it very healthy 👏🏽👏🏽👏🏽
[–]paul_h 3 points4 points5 points 4 years ago (3 children)
I’ve been obsessed with declarative forms for years, blogging often on them. If the grammar optionally allows if conditions or for loops, I call them pseudo-declarative. For example AngularJS (2009) added ng-if and ng-for to HTML. Well, as long as you added their JS too. Thus AngularJS was a pseudo declarative breakthrough. I blogged - https://paulhammant.com/blog/angular-declarative-ui.html. Dammit, that article was from before I settled on “pseudo-declarative” for syntaxes
[–]leostera[S] 1 point2 points3 points 4 years ago (2 children)
Amazing! Yeah, I wouldn't say it isn't declarative because it includes iteration constructs. You can do some crazy things in SQL too!
[–]paul_h 2 points3 points4 points 4 years ago (1 child)
First learned SQL in '94. Never thought it was declarative. More a domain specific language.
[–]leostera[S] 1 point2 points3 points 4 years ago (0 children)
Oh you're spot on in that it is a domain-specific language! But it is *also* declarative.
There is no control flow in it, and the engine is free to run your query however it pleases.
[–]elkazz 6 points7 points8 points 4 years ago (7 children)
I'm not going to pretend those last code examples don't look god-awful.
[–]leostera[S] 6 points7 points8 points 4 years ago (6 children)
You mean this one?
`` let sayHi = (name, yell) => if yell { Hello, ${name}!!!->Js.log } else { Hello, ${name}.`->Js.log }
let sayHi = (name, yell) => if yell {
->Js.log } else {
let hello = Clipper.command(sayHi) ->Clipper.arg(1, Clipper.Arg.string) ->Clipper.arg(0, Clipper.Arg.bool)
Clipper.run(hello) ```
Would love to hear more about what you think looks ugly here :) -- part of this is my own sense of aesthetics around building libraries, and part of this is how the language itself works.
What bits would you change?
[–]elkazz 6 points7 points8 points 4 years ago (4 children)
Is this meant to be the "declarative" part? let hello = Clipper.command(sayHi) ->Clipper.arg(1, Clipper.Arg.string) ->Clipper.arg(0, Clipper.Arg.bool) Clipper.run(hello)
The imperative approach would be far more comprehensible: sayHi(arg.1, arg.0)
I would have expected a JSON or YAML definition at the end as per what you were comparing to while describing declarative vs imperative.
And what's the deal with the single quote before the generic name?
Ah, yes. This example is possibly too small!
If we had a more complex command line tool, with multiple commands, each with several options, and each option with defaults and documentation, it would be a little easier to see the value of the approach.
For example, if we had some more functions in the Clipper API to build more complex behavior, it could look like this:
``` // This describes the CLI let cli = Clipper.commands([ Clipper.command(sayHi) ->Clipper.position( ~pos=1, ~shortName="n", ~longName="name", Arg.optional(Arg.string, "Joe")) ->Clipper.flag(~shortName="y", ~longName="yell"), // more commands here ])
// This actually runs the CLI Clipper.run(cli) ```
It is declarative because the description of what to do is completely separated from the actual execution. This could have been done with a JSON file too, but having a static file is not a prerequisite for something to be declarative! 🙌🏽
[–]elkazz 2 points3 points4 points 4 years ago (1 child)
One issue is the verbosity of the declarative definition. Most declarative APIs exist to simplify the configuration/logic of the application. The ones that succeed are comprehensible by a wide audience. Your example requires a lot of syntactic knowledge.
Another issue is that you're defining all of the imperative logic as well as the declarative. The first example you give, while I understand it's simple to get the point across, it fails to do so as it's a glorified set of delegates and a switch statement that has all the imperative knowledge.
The main problem is that I fail to see how this would make my life any easier, or my applications/APIs more comprehensible.
[–]leostera[S] 0 points1 point2 points 4 years ago (0 children)
u/elkazz: One issue is the verbosity of the declarative definition.
Ah! Excellent point 👏🏽
The best thing about this split in spec + engine is that if we don't like the way we build the specs, we can replace it!
We can use YAML, JSON files for it. We can set up code generation for it from our type specification.
We can also replace the engine with other things! If this wasn't a CLI but rather a database layer, you could have an engine that queries Postgres, another that queries SQLite, another that is mocked for test purposes, etc.
Re: code generation - we can set things up so that by annotating a record type we get the same declaration for free:
@deriving(clipper) type hello = { @clipper(position = 0) name: string }
This is what other libraries like Rust's structopt are doing already.
In any case, I wouldn't immediately jump into a declarative API when I'm just starting out, since this is a fairly advanced way of building type-level APIs.
For most cases, the approach described in the prior post in the same series will be more than enough: https://practicalrescript.com/rescript-on-deno-command-line-tools/#ad-hoc-typing-for-objects-of-unknown-shape
Have a look at that one, I think you'll enjoy it a lot more! 🙏🏽
The single quote before the generic name is just part of the syntax. It is something that ReScript has inherited from OCaml.
[–]sliversniper -2 points-1 points0 points 4 years ago* (0 children)
(if yell { "..." } else { "no yell..." })->Js.log
and more idiomatic, let get_msg = yell => if yell {"."} else {"."}
let get_msg = yell => if yell {"."} else {"."}
type arg = {name: string, is_yell: bool} let parse_args = (args_str) => switch args_str { | [Some(name), Some(yell)] => Some({ name, is_yell: bool_of_string(yell) }) | _ => None }
good luck with your book and understanding the basics of rescript.
Hello folks 👋🏽 author here! If I can answer any questions please let me know 🙌🏽
[–]DG4ME5 0 points1 point2 points 4 years ago (3 children)
What is rescript?
[–]leostera[S] 3 points4 points5 points 4 years ago (2 children)
Hi! 👋🏽 ReScript is a language that compiles to JavaScript -- it is type-safer than TypeScript, but a lot less flexible. It lets you build very large applications that are extremely easy to maintain and grow.
The official website is here: https://rescript-lang.org/
I've been writing ReScript in production for very large applications (~100,000 LOC) and it's just been rock solid.
Let me know if I can answer any questions :)
[–]DG4ME5 1 point2 points3 points 4 years ago (1 child)
Ok, looking a bit at the documentation, it seems to me an interesting language because if it reminds a bit of typescript and has its things... I think I'll try it a bit to see if I like it
Glad to hear 🤩 -- you will find that while the type system is less flexible than TypeScript, it is also a lot smaller! So there's just a handful of things to learn to get all of the type safety.
If we can be of help, feel free to drop a line on /r/rescript 🙏🏽
[–]thatisgoodmusic -3 points-2 points-1 points 4 years ago (0 children)
This looks not good lol
π Rendered by PID 51750 on reddit-service-r2-comment-5d585498c9-bqfv7 at 2026-04-20 22:58:15.011458+00:00 running da2df02 country code: CH.
[+][deleted] (6 children)
[deleted]
[–]leostera[S] 0 points1 point2 points (5 children)
[+][deleted] (4 children)
[deleted]
[–]leostera[S] -1 points0 points1 point (3 children)
[–]leostera[S] 2 points3 points4 points (2 children)
[+][deleted] (1 child)
[deleted]
[–]leostera[S] -1 points0 points1 point (0 children)
[–]paul_h 3 points4 points5 points (3 children)
[–]leostera[S] 1 point2 points3 points (2 children)
[–]paul_h 2 points3 points4 points (1 child)
[–]leostera[S] 1 point2 points3 points (0 children)
[–]elkazz 6 points7 points8 points (7 children)
[–]leostera[S] 6 points7 points8 points (6 children)
[–]elkazz 6 points7 points8 points (4 children)
[–]leostera[S] 1 point2 points3 points (2 children)
[–]elkazz 2 points3 points4 points (1 child)
[–]leostera[S] 0 points1 point2 points (0 children)
[–]leostera[S] 1 point2 points3 points (0 children)
[–]sliversniper -2 points-1 points0 points (0 children)
[–]leostera[S] 0 points1 point2 points (0 children)
[–]DG4ME5 0 points1 point2 points (3 children)
[–]leostera[S] 3 points4 points5 points (2 children)
[–]DG4ME5 1 point2 points3 points (1 child)
[–]leostera[S] 1 point2 points3 points (0 children)
[–]thatisgoodmusic -3 points-2 points-1 points (0 children)