It's Vue, it's PureScript. Simple as that.
<!-- Main.vue -->
<template>
<button @click="increment">
Count is: {{ count }}
</button>
<template>
<script lang="purescript">
import Prelude
import Effect (Effect)
import Effect.Console (log) as Effect.Console
import PureVue (UnwrapRef, ref, readonly, expose, set, get, watch)
count :: UnwrapRef "count" Int
count = ref 0
increment :: UnwrapRef "increment" (Effect Unit)
increment = readonly $ do
value <- get count
set count (value + 1)
log :: Effect Unit
log = do
value <- get count
Effect.Console.log ("count changed to: " <> value)
setup :: Effect Unit
setup = do
expose count
expose increment
discard $ watch count log
</script>Normally we use ELM based design to deal with JS rendering libraries in PureScript, but I would like to try a new approach with something more similar to Vue Composition API + SFC.
The central idea is, instead of dealing with Vue as a side-effect free library, use a DSL to compute things and interact with components through the setup hook of Vue components. We are still studying the feasability and effort of doing this without bringing too much noise to the code.
The build process would be something like this:
vite build -> SFC compiler -> PureVue (PS -> JS) -> SFC compiler (embed setup hook) -> component JS module
The advantage of doing this way is that the boilerplate required to build Vue SFC with PureScript is almostly done (with Vite). It also keeps the API isomorphic with Vue allowing a much smaller learning path to PureScript.
- PureScript only allows side-effects inside the
Effectmonad, for that reason we can only access or mutate aRefvalue inside anEffectmonad. - Differently from Vue, we don't expect that you use
refinside setup hook, ourrefis just a type constructor and does not generate side-effects, the same applies forreadonly. - To reduce boilerplate on PureScript side, all functions on the Vue side that receives a
Ref, on PureScript side, receives a correspondingUnwrapRef. setupdoes not receive arguments, to access their values use the respective functions:usePropsanduseContext.setupcan only return a wrapped render function (TODO: render type) orUnit, to define the template bindings useexposefunction.exposeis a effectful function which exposes aUnwrapRefto the Vue template as a reactiveRef (unwrap)value.
- We don't define a module with
modulekeyword, the module definition is set by the SFC compiler before purs compilation. - The only exported function is
setup, that is used as setup hook in component options by the SFC compiler.
There are a few reasons why I'm giving up on this project, after a very insightful discussion on PureScript Discord:
- I realized the complexity of building a type-checking solution. Besides being outside the scope of the proof of concept, it would be essential to make this solution usable in any sense, and I don't have any expertise in this topic.
- To have type-checking in place, we would need to fork
@vue/language-coreto have avue-pursalternative akin tovue-tsc. This would demand even a fork of the Vue language server itself. @vue/language-coreis currently entangled with TypeScript (see).