pure-vue
pure-vue copied to clipboard
It's Vue, it's PureScript. Simple as that.
pure-vue
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.
Differences from Vue
- PureScript only allows side-effects inside the
Effect
monad, for that reason we can only access or mutate aRef
value inside anEffect
monad. - Differently from Vue, we don't expect that you use
ref
inside setup hook, ourref
is 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
. -
setup
does not receive arguments, to access their values use the respective functions:useProps
anduseContext
. -
setup
can only return a wrapped render function (TODO: render type) orUnit
, to define the template bindings useexpose
function. -
expose
is a effectful function which exposes aUnwrapRef
to the Vue template as a reactiveRef (unwrap)
value.
Differences from PureScript
- We don't define a module with
module
keyword, 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.
Why I've Given Up on This Project
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-core
to have avue-purs
alternative akin tovue-tsc
. This would demand even a fork of the Vue language server itself. -
@vue/language-core
is currently entangled with TypeScript (see).