Unexpected behavious when using `[@bs.deriving abstract]`



I have read through these bucklescript docs and am hoping it will allow me to use a javascript library that manages state (and thus returns big javascritp objects).

Here is the javasript code (that works as expected):

import React from "react"
import { drizzleReactHooks as dHooks } from "drizzle-react"

let getWeb3FromState = fullState => fullState.web3

export default () => {
  let useDrizzleStateFunction = dHooks.useDrizzleState

  let web3 = useDrizzleStateFunction(getWeb3FromState)

  let status = web3.status

  return <h1> ...use the web3 object...  </h1>

And here is the reason code, that I expect would do the same thing but it doesn’t:

[@bs.deriving abstract]
type drizzleWeb3State = {
  status: string,
  networkId: int,

[@bs.deriving abstract]
type drizzleFullState = {web3: drizzleWeb3State};

[@bs.deriving abstract]
type drizzleHooks = {
  useDrizzleState: (drizzleFullState => drizzleWeb3State) => drizzleWeb3State,

[@bs.module "drizzle-react"]
external dHooks: drizzleHooks = "drizzleReactHooks";

let getWeb3FromState = fullState => fullState->web3Get;

let make = () => {
  let useDrizzleStateFunction = useDrizzleStateGet(dHooks);

  let web3 = useDrizzleStateFunction(getWeb3FromState);

  let status = statusGet(web3);

  <div className=Styles.app>
    <h1> {ReasonReact.string(" ...use the web3 object... ")} </h1>

The reason code prints the following into the console:

The javascript prints what is expected:

I have tried to keep the example simple, but if I go with this approach I’ll have to do this for more complicated objects. And I don’t know enough about record mode to know if this approach will cause me greater problems in the future.

I fear that I will simply have to write this code in javascript, but any advice would be welcomed!

Thanks :slight_smile:


The trick is to tell BuckleScript to uncurry the callback function that you pass in to useDrizzleState. See https://bucklescript.github.io/docs/en/function#curry-uncurry for an explanation of why certain functions need to be uncurried.

But more fundamentally, in this code you don’t really need to describe all your data with [@bs.abstract] types. BuckleScript supports inferring the type of JavaScript objects correctly simply from their usage. For example, instead of declaring the type drizzleFullState beforehand and then writing a function fullState => fullState->web3Get,[1] you can write just fullState => fullState##web3 thanks to this type inference support.

Here’s how I would recommend writing your example:

module DrizzleReact = {
  module Hooks = {
    [@bs.module "drizzle-react"] [@bs.scope "drizzleReactHooks"]
    external useDrizzleState: ([@bs.uncurry] 'a => 'b) => 'b = "";

let make() = {
  let web3 = DrizzleReact.Hooks.useDrizzleState(fullState => fullState##web3);

  <h1>{React.string("... use the web3 object ...")}</h1>;

[1] Btw in general a function literal like fullState => fullState->web3Get, or in other words fullState => web3Get(fullState), can be reduced to just web3Get. They’re equivalent function literals!


Thank you so much! That is a huge help :slight_smile:

Reasons type inference continues to amaze me!

Also, thanks for the great link on currying!


Seems like the ## syntax will be deprecated.

It definitely looks more tedious writing types for everything. Any idea why they are planning to deprecate it?


I believe that’s more a suggestion than any hard deadline that the feature will be removed. There’s too much code out there that would break if BuckleScript stopped supporting JavaScript property access syntax :slight_smile: Not only that, but it would be a breaking change they couldn’t simply push out a codemod for and upgrade users, because [@bs.abstract] simply doesn’t map cleanly to direct property access type inference.