A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/paluh/purescript-js-object below:

purescript-codegen/purescript-js-object: Simple helpers to build FFI for js object methods and properies.

Access js object methods and properties without writing JS bindings... ...or just generate mutable JS object FFI (without any codegen) from the type.

In PureScript we usually write FFI to object methods by implementing dedicated functions on both sides. It seems that we can provide set of generic helpers which without sacrificing the performance which are able to bind to properties and methods of a JS object (by using "uncurried" approach similar to Effect.Uncurried from purescript-effect under the hood).

Let's imagine that we have a simple counter prototype defined on the JS side and we expose an "effectful" function which creates an instance for us (we are not able to use new directly from PS side):

exports.counter = (function () {
  let Counter = function () {
    this.value = 0;
  };
  Counter.prototype.increase = function () {
    this.value++;
  };
  Counter.prototype.decrease = function () {
    this.value--;
  };
  // I'm working to cover constructors as well soon.
  // At the moment we need to expose a function ourselves.
  return function () {
    return new Counter();
  };
})();

Now we should be able to bind to this interface using generic helpers provided by this library:

import Prelude

import Data.Newtype (class Newtype)
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import JS.Object (EffectMth0, EffectMth1, EffectProp, JSObject, runEffectMth0, runEffectMth1, runEffectProp)
import JS.Object.Generic (mkFFI, mkNewtypedFFI)
import Test.Spec (describe, it)
import Test.Spec.Assertions (shouldEqual)
import Test.Spec.Reporter (consoleReporter)
import Test.Spec.Runner (runSpec)
import Type.Prelude (Proxy(..))
import Type.Row (type (+))

type Counter = JSObject (increase :: EffectMth0 Unit, decrease :: EffectMth0 Unit, value :: EffectProp Int)

foreign import counter :: Effect Counter


-- You can generate this FFI record during the compilation time.
-- There is no runtime footprint over the manual binding.
-- Type signature is derived automatically but because we don't
-- use newtype... yet it would be fully expanded by default.
_Counter ::
  { increase :: Counter -> Effect Unit
  , decrease :: Counter -> Effect Unit
  , value :: Effect Int
  }
_Counter = mkFFI (Proxy :: Proxy Counter)


-- If you want you can use newtypes as well.
-- Here is a binding for hypothetical Person `JSObject`.
newtype Person = Person
  ( JSObject
      ( firstName :: EffectProp String
      , setFirstName :: EffectMth1 String Unit
      , lastName :: EffectProp String
      , setLastName :: EffectMth1 String Unit
      )
  )

derive instance Newtype Person _

_Person ::
  { firstName :: Person -> Effect String
  , lastName :: Person -> Effect String
  , setFirstName :: Person -> String -> Effect Unit
  , setLastName :: Person -> String -> Effect Unit
  }
_Person = mkNewtypedFFI (Proxy :: Proxy Person)


-- You can also use lower level functions to construct bindings yourself.
increase :: Counter -> Effect Unit
increase = runEffectMth0 (Proxy :: Proxy "increase")

decrease :: Counter -> Effect Unit
decrease = runEffectMth0 (Proxy :: Proxy "increase")

value :: Counter -> Effect Int
value = runEffectProp (Proxy :: Proxy "value")

main :: Effect Unit
main = launchAff_ $ runSpec [ consoleReporter ] do
  describe "JS.Object" do
    it "property access" do
      c <- liftEffect counter
      v <- liftEffect $ value c
      v `shouldEqual` 0
    it "method call" do
      v <- liftEffect $ do
        c <- counter
        increase c
        increase c
        v <- value c
        pure v
      v `shouldEqual` 2

There are two nice properties of this generic method of binding to JS object:

type IncreaseInterface r = ( increase :: EffectMth0 Unit | r)

type DecreaseInterface r = (decrease :: EffectMth0 Unit | r)

type ValueInterface r = (value :: EffectProp Int | r)

increase :: forall r. JSObject (IncreaseInterface r) -> Effect Unit
increase = runEffectMth0 (Proxy :: Proxy "increase")

decrease :: forall r. JSObject (DecreaseInterface r) -> Effect Unit
decrease = runEffectMth0 (Proxy :: Proxy "decrease")

value :: forall r. JSObject (ValueInterface r) -> Effect Int
value = runEffectProp (Proxy :: Proxy "value")

type Counter = JSObject (IncreaseInterface + DecreaseInterface + ValueInterface + ())
$ spago --config devel.dhall test

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4