GithubHelp home page GithubHelp logo

typed-fennel's Introduction

typed-fennel

Adding dynamic type checking to Fennel. This library contains replacements for common built-in forms that accept type annotations and perform type checking at runtime.

Installation

Clone this repository and place it in your package path, e.g. the directory containing your code.

git clone https://github.com/dokutan/typed-fennel

Usage

(import-macros {: fn>} :typed-fennel)
(local {: has-type?} (require :typed-fennel))

;; simple type checking
(has-type? 1 :integer)        ; => true
(has-type? 1 :float)          ; => false
(has-type? [1 2] [:any :any]) ; => true

;; create a type checked function
(fn> inc [a :number] [:number] (+ a 1))
(inc 1)   ; => 2
(inc "1") ; => runtime error

;; use let>, local> and var> for type checking
(var> x :string "hello")
(let> [a :number 10
       b :number 20]
  (+ a b))

;; destructuring is supported by all macros
(local> (a b) [:string :any] (foo))
(fn [a & rest] [:any :table]
  (print a))

Type system

Primitive types

The available primitive types are based on the normal Fennel/Lua types, with some additions. They are represented as strings.

  • :nil
  • :string
  • :number
  • :boolean
  • :table
  • :function
  • :thread
  • :userdata
  • :integer
  • :float
  • :file
  • :closed-file
  • :any

Nilable types

All primitive types can be prefixed with a question mark, e.g. :?number, to allow the corresponding value to be nil. This also works for :?nil and :?any, which has no effect on the type, but could be useful for annotating optional function parameters.

(has-type? 1 :?number)   ; => true
(has-type? nil :?number) ; => true
(has-type? "1" :?number) ; => false

Table types / table schemas

Tables of types can be used to describe tables:

(local {: has-type?} (require :typed-fennel))

(has-type? [1] [:number :number])     ; => false
(has-type? [1 2] [:number :number])   ; => true
(has-type? [1 2 3] [:number :number]) ; => true

(has-type? [1 [2]] [:number [:number]]) ; => true

(has-type? {:x 1 :y 2} {:x :number :y :number}) ; => true

List/sequential types

Use (seq T) to create a type that describes a sequential table containing 0 or more elements of type T.

(local {: has-type? : seq} (require :typed-fennel))

(has-type? [] (seq :number))        ; => true
(has-type? [1] (seq :number))       ; => true
(has-type? [1 2] (seq :number))     ; => true
(has-type? [1 2 "3"] (seq :number)) ; => false

Enums

Use enum to create a type that allows a variable to take only one of the given values.

(local {: has-type? : enum} (require :typed-fennel))

;; A weekday enum
(local weekday
  (enum
    :monday
    :tuesday
    :wednesday
    :thursday
    :friday
    :saturday
    :sunday))

(has-type? :monday weekday) ; => true
(has-type? :foo weekday)    ; => false

Type functions

Any function (or table with the __call metamethod) that accepts one value and returns a truthy value can be used as a type.

(import-macros {: fn>} :typed-fennel)
(local {: has-type?} (require :typed-fennel))

(fn positive-int [value]
  "A positive integer type"
  (and
    (has-type? value :integer)
    (>= value 0)))
(has-type? 1 positive-int)  ; => true
(has-type? -1 positive-int) ; => false

(fn byte [value]
  "A bounded type: 0-255"
  (>= 255 value 0))
(has-type? 255 byte) ; => true
(has-type? 256 byte) ; => false

Union types

Union types can be constructed using the union function:

(import-macros {: fn>} :typed-fennel)
(local {: has-type? : union} (require :typed-fennel))

(local number-or-string (union :number :string))
(has-type? 1 number-or-string)   ; => true
(has-type? "1" number-or-string) ; => true

(fn> length2 [a (union :string :table)] [:integer] (length a))
(length2 "123") ; => 3
(length2 123)   ; => runtime error

Intersection types

Union types can be constructed using the intersection function:

(import-macros {: fn>} :typed-fennel)
(local {: has-type? : intersection} (require :typed-fennel))

(local ascii-char (intersection :string #(= 1 (length $))))
(has-type? :a ascii-char) ; => true
(has-type?  ascii-char) ; => false

Using expressions as types

In the fn> macro any expression evaluating to a type can be used, this allows e.g. generics to be implemented. When the macro is expanded, the expressions have access to the function parameters:

(import-macros {: fn>} :typed-fennel)

(fn> tuple [a :any] [[(type a) (type a)]] [a a])

(fn> assert-same-type [a :any b (type a)] [:boolean] true)
(assert-same-type 1 2)   ; => true
(assert-same-type :1 :2) ; => true
(assert-same-type 1 :2)  ; => runtime error

typed-fennel's People

Contributors

dokutan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

reo101

typed-fennel's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.