It depends what you’re trying to achieve, but in ReScript it’s not typical to make use of objects that contain this references.
For example, conventionally you might use something like this:
type store = {
price: int,
amount: int,
}
let getTotal = store => {
store.price * store.amount
}
let store = {
price: 2,
amount: 4,
}
let total = getTotal(store)
But if you need to have literally that object, then you could use %raw() to escape into regular JS and write it that way:
type store
@send external getTotal: store => int = "total"
let makeStore: (int, int) => store = %raw(`
function makeStore(price, amount) {
return {
price: price,
amount: amount,
total() {
return this.price * this.amount
}
}
}
`)
let store = makeStore(2, 4)
let total = getTotal(store)
Lastly, a bit more verbose syntax can be used to using the @this decorator. A good example is over on the Syntax Lookup page:
kindsun,
Though there are some cases for having methods like youre showing, try separating the data and the behavior:
module Store = {
// When we say Store we have one piece of data in mind: a record of price and amount.
// as short hand, with no other types defined in this module, it will often have the name `t`
type t = {
price: int,
amount: int
}
// If you have a value of type t, we can tell you its total.
// the total function is not replaceable per store value, but youre welcome to call different functions =)
let total = (t: t) =>t.price * t.amount
}
let total = Store.total({price: 2, amount: 4})
Thanks @kevanstannard and @mouton! I suspect the simplest solution would be something along the lines of falling down into a %raw block for what I need here.
Since you both made reasonable comments along the lines of separating the logic, let me give some more context. In this particular case I’m handing off the object to an external lib which expects a certain shape js obj. I actually think a module would make the most sense here @mouton but it seems that if I were to pass a module out externally, I would need a way of binding a functor to external code which would then take the module as an argument. I did go down this rabbit hole but couldn’t quite get anything working. I’m staring at the documentation to determine if this is actually a reasonable solution. I can reattempt it in parallel, but is this possible?
In doing Highcharts bindings I have hit something similar where the their chart configuration takes a pointFormatter function that is later invoked with this bound to a point object. My solution was to make the Highcharts bindings thicker by adding a function to convert this into an explicit argument, and then decorate the call where the non-this function is passed in, and apply convertThisToArgPoint before passing down to JS
// Highcharts does not pass any parameters to the formatter, but
// apparently is called within the context of a "Point" object, and seems
// to require defining an inline function here so can use `this.` references
// see https://api.highcharts.com/highcharts/tooltip.pointFormatter
let convertThisToArgPoint: ((. point) => string) => ((unit) => string) = %raw(`
(f) => {
return function() {
return f(this)
};
}
`)
I actually think I may have it. The solution ended up being first-class modules which CAN be passed via a normal function. Once I updated my bindings to handle them, things actually worked pretty well.
It’s still rough around the edges in terms of typing, flexibility and completeness, but it is working as expected! Here’s a more complete picture of what I’ve been working on, Mobx bindings:
// Updated bindings
module type Store = {
let total: unit => int
}
@module("mobx") external extendObservable: ({..}, 'a) => 'a = "extendObservable"
let observable = (module(Store: Store)) => {
let newStore = Js.Obj.empty()
let store: module(Store) = module(Store)
extendObservable(newStore, store)
}
// Store with mutable properties
module Store = {
let price = ref(5)
let amount = ref(6)
let total = () => price.contents * amount.contents
// ...future functions that modify price & amount
}
let _ = observable(module(Store))
lol, I guess it’s all relative, but asking as fundamental a question as “is there a this equivalent” seemed beginner-ish to me. Things just kind of wandered from there. Also, while the bulk of rescript makes intuitive sense to me, anytime I’m up against anything even slightly advanced involving js interop, I feel very much like a beginner!
Thanks for nudging me to think in the direction of modules again!
Ah I thought price and amount had to be plain number fields in this object. If you’d said the only fixed part was the total method, I might’ve suggested this earlier