Thank you I have to experiment with this, it’s really interesting. What is it called?
What is what called? Some of the concepts involved here are first-class modules, locally abstract types and module constraints with type equations. If I remember all the terminology correctly.
I have a small opensource project. Maybe it’ll give you some inspiration
How can I ensure Belt is always loaded for every file?
Or is it even a good idea? What bugs me the most is that opening Belt changes array access (for the better, but it’s confusing that opening a module changes array access everywhere). Help me think about this - how do you guys do it?
There’s a config option in bsconfig.json to open belt in every file. Check out the “Usage” in the belt introduction
Yeah, I saw that - thanks. Is that recommended - is that something that you usually see in ReScript projects?
That page mentions it’s the only use of open they recommend, so I assume it’s recommended. Though, for me personally I followed what someone else here mentioned and I make my own StdLib.res
and bend it more a bit to my needs, and then open StdLib
at the top of every file
Example in my current project
module A = {
include Belt.Array
let reduce = Js.Array2.reduceRighti
let find = Js.Array2.find
let joinWith = Js.Array2.joinWith
let findIndex = (a: array<'a>, pred: 'a => bool) =>
switch a->Js.Array2.findIndex(pred) {
| -1 => None
| idx => Some(idx)
}
let last = (a: array<'a>): 'a =>
a->getExn(a->length - 1)
let sortBy = (a: array<'a>, map: 'a => int): array<'a> =>
a->copy->Js.Array2.sortInPlaceWith((a, b) => map(a) > map(b) ? -1 : 1)
let sortByF = (a: array<'a>, map: 'a => float): array<'a> =>
a->copy->Js.Array2.sortInPlaceWith((a, b) => map(a) > map(b) ? -1 : 1)
}
Interesting, thanks, But you need to rename that file to NonStdLib.res.
I’ve started following the same approach with custom standard library in my personal projects. The only thing that’s stopping me to introduce it at work is some sort of ReScript linter that would complain on Js
/Belt
usage.
How can I easily turn a polymorphic variant into a string?
Whatever Error is reported by a function, I want to display it as a string. It will be one of #NoSuchUser, #Forbidden, #Unauthorized etc. Can I easily turn it into a string representation without needing a toString-function that handles each case separately?
The autoformatter is starting to drive me crazy. How can I possibly change the linewidth? It’s super annoying that I can’t fit a full line on my screen. I usually work in split-screen mode with 60% for coding and the rest for viewing the app that I’m coding.
There are few open tickets in the syntax repo, here’s a couple of data points from last year. There isn’t any way to change it at the moment.
I always liked thinking about code, a sort of meta-programming perspective. What is code, what are code constructs, etc.
What kind of constructs do you use regularly that you find helpful?
And also
What naming conventions do you use?
It seems ReScript has adopted JS naming style rather than ML naming style (camelCase vs snake_case). I tend to prefer snake case, but it doesn’t really sit well since everyhing I interact with is camel case. How do you name your things?
Most particularly, what about polymorphic variants? I’ve seen Error(#NO_SUCH_USER), #NoSuchUser, #No_such_user and #no_such_user. Which ones do you use and why?
Do you name your modules like Namespace_MyModule or Namespace__My_module?
What about functions?
And finally:
How do you make your “method calls” look alright? Yes, they are not true method calls, but they are used like it:
log->Log.write(“hello world”)
I prefer log#write(“hello world”) but we don’t have that option in ReScript - what are you guys’ take on this?
The most basic ones so everybody understands.
I’ve rarely seen snake case in ReScript ecosystem, I’d even say camelcase is a standard.
We use uppercase for polymorphic variants. It usually depends on the background developers came from. It’s mostly JavaScript in our case.
The first one. And if it’s not a library Namespace_
, but an application it’s better to give a unique name instead of prefixing with namespaces.
Camelcase as well.
This one log->Log.write(“hello world”)
looks alright to me. My personal convention is “If the first argument is t
, then pass it with pipe, else it should be a labeled argument”.
What’s a good workflow for “explorative coding” and prototyping with ReScript?
I definitely find that ReScript reduces the number of runtime errors. Usually, if it compiles, it doesn’t crash. For prototyping, however, I feel like I’m losing soooo much productivity. How do you handle this? Is ReScript not suited well for it, or am I doing something wrong? Will it come with time?
This is something pretty interesting to understand better.
Some people claim they would like to do explorative coding, and they cannot do it with types.
Others say they want types when making quick prototypes as it helps them avoid basic mistakes.
Wondering whether there are really 2 different use cases under the umbrella “explorative coding”, or just different people are affected differently.
My personal curiosity is: why not just plain JS for explorative coding?
Yes, I considered this, and I actually tried it when implementing an API endpoint. What happened:
I experimented back and forth. In the end, it worked fine. I ended up with a solution that’s “perfect” for what I needed to achieve. Then what? I throw away the code and reimplement it?
No, what I felt was, hey, it’s doing the job, it’s neat and understandable and likely will not change in a long time. So I might as well just leave it there as JS.
I don’t think “hey, just do it in JS until it works and then do it again in ReScript” is a viable solution. I mean, you could pick any two languages. “Oh, you need to draw some stuff? Yeah, just go write it in C with SDL and when you’re happy come back and do it in JS with a canvas.” If I manage to implement it well in another language, why come back and rewrite it in a different language? Just so that the types are there for the future, or what? If I wanted to do what you’re suggesting, I would probably first implement it in TS without annotations (i.e., JS) and then add annotations.
Yes, I agree. It’s an interesting issue, if it even is one. I mean I could imagine you could be saying the same thing for Rust. How do they address this? Is it a matter of competence and experience? Or the way I do my own hackathons?
The same issue seems to exist for Rust, which is not surprising. But then again, Rust is a systems programming language.
I think what I would like for ReScript is a “stop bothering me for a bit” mode during prototyping. Thinking of it, TypeScript does this, but the other way around. It’s default mode is “go bananas with JS/no annotations” and then when it works the incentives to add types are just not really there.
What if ReScript could do it the other way around? “I’m going to be strict with you, but since you’re hacking something together, feel free to play for a bit”.
What about some // @typeless
pragma at the top of the file or similar? Typing turns everything into magic objects that you can do anything with. For production mode - forget about it. It will cause errors and the build to fail or whatever, you have to enable type inference again and it’s going to be as strict as ever, and then you have to make sure to fix the type safety issues in your code (if any).