New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@funkia/funnel

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@funkia/funnel

A functional frontend framework in Typescript

  • 0.0.7
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
2
increased by100%
Maintainers
1
Weekly downloads
 
Created
Source

Funnel

A purely functional frontend framework based on functional reactive programming. Experimental.

Build Status codecov Sauce Test Status

Ideas/features

The goal of Funnel is to be a powerful framework for building frontend applications in a purely functional way. Funnel is based on FRP and is heavily inspired by functional techniques found in Haskell.

  • Purely functional.
  • Implemented in TypeScript. Later on we'd like to support PureScript as well.
  • Based on classic FRP. Behaviors represents values that changes over time and streams provide reactivity. Funnel uses the FRP library Hareactive.
  • A component-based architecture. Components are encapsulated and composable. Components are monads and are typically used and composed with do-notation (do-notation is implemented with generators).
  • Constructed DOM elements reacts directly to behaviors and streams. This avoids the overhead of using virtual DOM and should lead to great performance.
  • Side-effects are expressed with a declarative IO-like monad. This allows for easy testing of effectful code. Furthermore, the IO-monad is integrated with FRP. This makes it possible to perform side-effects in response to user input.
  • The entire dataflow through applications is explicit and easy to follow.

High level overview

FRP as building blocks

Funnel builds on top of the FRP library Hareactive. Two of the key concepts from FRP are:

  • Behavior — Represents values that change over time.
  • Stream — Represents discrete events that happen over time.

They are documented in more detail in the Hareactive readme.

What is Component

On top of the FRP primitives Funnel adds Component. Components can contain logic expressed through combinations of behaviors and streams. They can run IO-actions and add elements to the DOM.

Component figure

Components in Funnel are encapsulated. They can have completely private state and selectively decide what output they deliver to their parent.

A component represents one or more DOM elements and the output they produce. For example, a Component that represents an input element can be created like this

const inputComponent = input();

The component has the type Component<Output> where Output is an object containing the output that an input element produces. Among other things an input element produces a string-valued behavior with the current content of the input element and a stream of keyboard events from the element.

Components are composable and combine into components. A Funnel app is just one big component. There is no difference between a top level component and child components. Components combine with their chain method. The signature of chain is

chain((output: Output) => Component<NewOutput>): Component<NewOutput>;

Example.

input().chain((inputOutput) => span(inputOutput.inputValue));

An invocation component.chain(fn) works like this:

  • The output from component is passed to fn.
  • fn returns a new component, let's call it component2
  • The DOM-elements from component and component2 are concatenated.
  • The result of the computation is a component with the concatenated DOM-elements and output equal to the output from component2.

So, the above example boils down to this:

Create input component   Create span component with text content
  ↓                             ↓
input().chain((inputOutput) => span(inputOutput.inputValue));
                   ↑                                ↑
      Output from input-element       Behavior of text in input-element

The result is an input element followed by a span element. When something is written in the input the text in the span element is update accordingly.

With chain we can combine as many elements as we'd like. This example combines a div with a span with a p.

div().chain((_) => span("Text").chain((_) => p("More text")));

And the resulting HTML would look like this:

<div></div>
<span>Text</span>
<p>More text</p>

However, when we don't use the output from components we can instead combine them with sequence_.

sequence_(Component [div(), span("Text"), p("More text")]);

Components typically take child components as their last argument. If instead of the above HTML we wanted this:

<div>
  <span>Text</span>
  <p>More text</p>
</div>

We could do

div(span("Test").chain((_) => p("More text")));

As a convenience we can also do

div([
  span("Test"),
  p("More text")
])

This is "sugar" for calling sequence_ on the array.

Often using chain can be cumbersome since each chain invocation adds a layer of nesting. Instead we can use "go-notation".

component = go(function*() {
  const {inputValue} = yield input();
  yield p(inputValue);
});

Example

The example below creates an input field and print whether or not it is valid.

import {map} from "jabz";
import {runMain, elements, loop} from "@funkia/funnel";
const {span, input, div} = elements;

const isValidEmail = (s: string) => s.match(/.+@.+\..+/i);

const main = go(function*() {
  yield span("Please enter an email address: ");
  const {inputValue: email} = yield input();
  const isValid = map(isValidEmail, email);
  yield div([
    "The address is ", map((b) => b ? "valid" : "invalid", isValid)
  ]);
});

// `runMain` should be the only impure function in application code
runMain("#mount", main);

A few explanations to the above code:

  • The go function and the generator expresses do-notation, i.e. monadic chaining. Here the monad is Component.
  • The function input returns Component<{inputValue: Behavior<string>}>. We yield it which binds the inputValue behavior to email.
  • Next the isValidEmail predicate is mapped over the email behavior and a div component describing the validation status is added.

Examples

Approximately listed in order of increasing complexity.

  • Simple — Very simple example of an email validator.
  • Fahrenheit celsius — A converter between fahrenheit and celsius.
  • Zip codes — A zip code validator. Shows one way of doing HTTP-requests with the IO-monad.
  • Continuous time — Shows how to utilize continuous time.
  • Counters — A list of counters. Demonstrates nested components, managing a list of components and how child components can communicate with parent components.
  • Todo — An implementation of the classic TodoMVC application. Note: Routing is not implemented yet.

Getting started

Installation

npm install @funkia/funnel

Funnel uses two peer dependencies, that you'll need to install too:

npm install --save jabz hareactive

Documentation

Nothing here yet. See the examples.

Contributing

Run tests once with the below command. It will additionally generate an HTML coverage report in ./coverage.

npm test

Continuously run the tests with

npm run test-watch

Keywords

FAQs

Package last updated on 09 Mar 2017

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc