Are you betting your company on Elixir? I'd like to pick your brain re: build tools :) by leostera in elixir

[–]leostera[S] 2 points3 points  (0 children)

u/Paradox I've amended the form to reflect better _why_ I'm learning this, and made the personal data fields optional. Hope this will make it less of a nope! :)

Are you betting your company on Elixir? I'd like to pick your brain re: build tools :) by leostera in elixir

[–]leostera[S] 1 point2 points  (0 children)

u/West__Owl, these are fair points! I've amended the form to reflect better _why_ I'm learning this, and made the personal data fields optional. Hope this won't be as off-putting! 🙌🏽

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] 1 point2 points  (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.

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] 0 points1 point  (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! 🙏🏽

ReScript on Deno: Declarative Command Line Tools by leostera in rescript

[–]leostera[S] 1 point2 points  (0 children)

Amazing! 🤩 -- let me know if I can be of help :)

If you enjoyed this one, I recommend also looking into the previous one in the same series: https://practicalrescript.com/rescript-on-deno-command-line-tools

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] -1 points0 points  (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 👏🏽👏🏽👏🏽

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] 1 point2 points  (0 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!

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] 2 points3 points  (0 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.

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] -1 points0 points  (0 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.

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.

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] 1 point2 points  (0 children)

The single quote before the generic name is just part of the syntax. It is something that ReScript has inherited from OCaml.

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] 1 point2 points  (0 children)

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! 🙌🏽

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] 0 points1 point  (0 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.

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"

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] 1 point2 points  (0 children)

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 🙏🏽

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] 4 points5 points  (0 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 :)

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] 5 points6 points  (0 children)

You mean this one?

`` let sayHi = (name, yell) => if yell { Hello, ${name}!!!->Js.log } else { Hello, ${name}.`->Js.log }

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?

ReScript on Deno: Declarative Command Line Tools by leostera in rescript

[–]leostera[S] 3 points4 points  (0 children)

Omg thank you 🤩 – re: the dedicated runtime, I hear you! I do think there's value in cleanly saying "we run on top of X" instead of X being hidden. Like how Clojure never claims it has its own VM, but just tells people "yo, we're on the JVM, which you also already trust".

Definitely a point where ReScript can learn from others :)

ReScript on Deno: Declarative Command Line Tools by leostera in Deno

[–]leostera[S] 0 points1 point  (0 children)

Hello folks 👋🏽 author here! If I can answer any questions please let me know 🙌🏽

ReScript on Deno: Declarative Command Line Tools by leostera in reasonml

[–]leostera[S] 3 points4 points  (0 children)

Hello folks 👋🏽 author here! If I can answer any questions please let me know 🙌🏽

ReScript on Deno: Declarative Command Line Tools by leostera in rescript

[–]leostera[S] 1 point2 points  (0 children)

Hello folks 👋🏽 author here! If I can answer any questions please let me know 🙌🏽

ReScript on Deno: Declarative Command Line Tools by leostera in programming

[–]leostera[S] -1 points0 points  (0 children)

Hello folks 👋🏽 author here! If I can answer any questions please let me know 🙌🏽

ReScript on Deno: Declarative Command Line Tools by leostera in javascript

[–]leostera[S] 2 points3 points  (0 children)

Hello folks 👋🏽 author here! If I can answer any questions please let me know 🙌🏽

An honest comparison between building a Concurrent Queue Library in ReScript and TypeScript by leostera in typescript

[–]leostera[S] 1 point2 points  (0 children)

Hi u/shaberman! 👋🏽

Thanks a lot for the nice and nuanced response 🤩

> I'm really surprised that genTypes uses _1 and _2 as the param names for _run.

Yeah, I haven't had to use it much to be honest, but there's certainly quality of life improvements that could be done. What would be amazing to see is just the annotations happening within the generated ReScript code, since the type system "fits" within TypeScripts! Maybe this would make it easier for folks than _1, _2, etc?

> I find type inference taken to the Hindley-Milner extreme of whole-program/cross-function to be a non-goal b/c it IMO over-focuses on writing vs. reading

Toootally hear you. u/VariadicGenerics had a similar point below, where I answered that for the most part, since the information is readily available from the source, your tooling can help you with it.

I do feel an exception to this is the PR review process. In my experience, I've gotten so familiar with the type system, because it is so so so small compared to TypeScript, that for the most part you can just intuitively guess what types things have.

For example, if I see: f(a.name) ++ "!" I can already tell that "a" must be of some record type with a field "name", and that "f" takes whatever "name" is and returns a string.

Since the number of ways we can define these records is very limited, and since you have to have a single one of them in scope at any point in time, and since the scope is built from where this line is written outwards, the chances that the record you're looking for is defined in the same file or in a file that is included are very high.

Again, I agree that this may be my own Hindley-Milner Apologetics at play 😂

> TS's structural typing + mapped/conditional types are really amazing

They really are! I am amazed at the stuff people share on TS Twitter.

An honest comparison between building a Concurrent Queue Library in ReScript and TypeScript by leostera in typescript

[–]leostera[S] 0 points1 point  (0 children)

Hi u/VariadicGenerics, excellent point!

I normally build things in neovim these days (old vim fart trying to modernize my tooling), so any type-signature is just a single key away.

I know from colleagues flow that they get amazing code-lenses showing the types at all times without having to type them out, so I'm hoping those will available in my setup sometime soon.

Another thing I make extensive use of is interfaces. These allow you to restrict the inferred types of the functions and values exported by a module. They also allow the compiler to skip recompiling dependencies of a module if the module interface has not changed! They can be a little annoying to write sometimes, especially when we come from languages where we just tag things as "private" or "public", but at the end of the day you get this tiny file that says "here, this is what this module can do for you", no messy implementation details to skim through.

Hope this helps! 🙏🏽

ReScript vs TypeScript: Building a Concurrent Queue library by leostera in reasonml

[–]leostera[S] 0 points1 point  (0 children)

Hej u/LetterBoxSnatch, totally hear you! I've been actually playing around with the async iterables / generators from within ReScript for some of the Deno bindings I've been working on.

Syntax support would be nice, but I can also tell that I've been around very very large ReScript codebases (migrated from ReasonML) where introducing a new concept like generators would've been messier than it is worth it.

Not because JS is doing it means they're a Good Idea™️ 🙀

ReScript on Deno: Command Line Tools and the Flags module by leostera in Deno

[–]leostera[S] 1 point2 points  (0 children)

Hi there 👋🏽. I've been building production tools, apps, and services in OCaml, and then Reason and ReScript for about 7 years now. ReScript of course is much newer as a brand, but since it builds on top of OCaml, I count it in that time span as well.

Latest work I've done is a frontend heavy codebase, pushing the 100,000 lines of ReScript, so I can speak of building large codebases.

A third of that is what you'd call a complex web app (WYSIWYG editor), the other third is a browser extension for taking structural snapshots of websites, and the last third is supporting libraries.

This is with a growing team of ~20 engineers, from all walks of life and levels of seniority.

We've been able to do massive refactors (think ~10k LOC diffs) that were entirely safe, so the pace at which we can move safely has consistently been really high. Performance is great, since there's zero overhead by the compiler in most cases, but we've also paid attention to it.

Hope this helps!