{"id":23441904,"url":"https://github.com/genivia/husky","last_synced_at":"2026-01-20T08:32:13.731Z","repository":{"id":114468054,"uuid":"291311149","full_name":"Genivia/Husky","owner":"Genivia","description":"🐺 Husky is a lazy functional language similar to Haskell, but with a more conventional syntax","archived":false,"fork":false,"pushed_at":"2020-09-21T14:08:01.000Z","size":125,"stargazers_count":17,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-24T23:48:59.638Z","etag":null,"topics":["functional-language","hindley-milner","lambda-calculus","lazy-evaluation","type-inference"],"latest_commit_sha":null,"homepage":"","language":"Prolog","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Genivia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-08-29T16:50:56.000Z","updated_at":"2025-08-24T00:44:58.000Z","dependencies_parsed_at":"2023-03-21T08:56:27.387Z","dependency_job_id":null,"html_url":"https://github.com/Genivia/Husky","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Genivia/Husky","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Genivia%2FHusky","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Genivia%2FHusky/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Genivia%2FHusky/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Genivia%2FHusky/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Genivia","download_url":"https://codeload.github.com/Genivia/Husky/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Genivia%2FHusky/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28599003,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T02:08:49.799Z","status":"ssl_error","status_checked_at":"2026-01-20T02:08:44.148Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional-language","hindley-milner","lambda-calculus","lazy-evaluation","type-inference"],"created_at":"2024-12-23T17:19:25.123Z","updated_at":"2026-01-20T08:32:13.712Z","avatar_url":"https://github.com/Genivia.png","language":"Prolog","readme":"Lazy functional programming with Husky\n======================================\n\n- [Husky syntax](#husky-syntax)\n- [Definitions](#definitions)\n- [Types](#types)\n- [Examples](#examples)\n- [Functions](#functions)\n- [Lambdas](#lambdas)\n- [List comprehension](#list-comprehension)\n- [Macros](#macros)\n- [Special constructs](#special-constructs)\n- [Commands](#commands)\n- [Gotchas](#gotchas)\n- [Installation](#installation)\n- [Author](#author)\n- [License](#license)\n\nHusky is a lazy functional language similar to Haskell, but with a more\nconventional syntax.  Husky implements a Hindley-Milner-style parametric\npolymorphic type system, higher-order functions, argument pattern matching for\nfunction specialization, currying, macros, list comprehension, and monads.\n\nHusky uses NOR (normal order reduction), WHNF (weak head normal form), sharing\n(an important lazy evaluation optimization), and lazy constructors to create\ninfinite \"lazy\" data structures.\n\nFor example, we can create an infinite list of integers, filter the even\nnumbers, and only pick the first ten even numbers:\n\n    \u003e take(10, filter(even, from(1))).\n    [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] :: [num].\n\nFunction `from(1)` returns the infinite list `[1, 2, 3, ...]`, `even(n)`\nreturns `true` if `n` is even, and `filter` retains all even values in the list\nusing `even` as a filter.\n\nHusky is written in SWI-Prolog using Prolog unification for efficient beta\nnormal-order reduction of the lambda `A -\u003e B` applied to argument `X`:\n\n    apply(A -\u003e B, X, B) :- !, (var(A) -\u003e A = X; eval(X, V) -\u003e A = V).\n\nThe `apply` operation takes a lambda `A -\u003e B` to apply to the unevaluated `X`,\nthen returns `B` when `A` is a variable.  If `A` is not a variable, then\npattern matching is used by evaluating `X` to `V` and unifying `A` with `V`.\n\nSharing is implemented internally by using an `eval(X, V)` functor that holds\nan (un)evaluated expression `X` and variable `V`.  When `X` is evaluated to a\nvalue, variable `V` is set to that value, thereby sharing the value of `V` to\nall uses of `V` (which are of the form of `eval(X, V)`) in an expression.\n\nThis reduction method makes Husky run fast, asymptotically as fast as Haskell.\nExcept that Husky does not optimize tail recursion, meaning that very deep\nrecursive calls will eventually fail.\n\nType inference is performed by Prolog inference.  Type rules are expressed as\nProlog clauses for the Husky type inference operator `::`.  These clauses have\na one-to-one correspondence to Post system rules that are often used to define\npolymorphic type systems with rules for type inference.  For example, the\npolymorphic type of a lambda is defined as a Prolog rule:\n\n    (A -\u003e B) :: Alpha -\u003e Beta :- A :: Alpha, B :: Beta.\n\nThis rule infers the type of a lambda as `Alpha -\u003e Beta` if argument `A` is of\ntype `Alpha` and `B` is of type `Beta`.\n\nThe type rule for lambda application (`:`) uses Prolog unification with the\n\"occurs check\" to safely implement the Hindley-Milner-style parametric\npolymorphic type system of Husky:\n\n    F:A :: Beta :- !, A :: Alpha, F :: Gamma, unify_with_occurs_check(Gamma, (Alpha -\u003e Beta))\n\nIt is that simple!\n\nThe Curry-Howard correspondence (the link between logic and computation)\ntrivially follows by observing that the plain unoptimized beta reduction rule\nis the same as the type inference rule for application without the occurs\ncheck:\n\n    apply(F, A, B) :- eval(F, (A -\u003e B)).\n    F:A :: Beta :- F :: (Alpha -\u003e Beta), A :: Alpha.\n\nIn both rules `A` and its type `Alpha` are unified with the lambda argument to\nproduce `B` and its type `Beta`.\n\nBecause Husky evaluation rules and type inference rules are defined in Prolog,\nthe implementation is easy to understand and change, for example to add\nfeatures, to change the language, and to experiment with type systems.\n\nHusky syntax\n------------\n\nHusky syntax follows the usual syntax of arithmetic expressions with functions.\nFunctions are applied to one or more parenthesized arguments:\n\n    func(arg1, arg2, ...)\n\nValues are Booleans `true` and `false`, numbers, strings (quoted with `\"`),\nlists written with `[` and `]` brackets, and arbitrary data structures.\n\nAt the Husky prompt you can enter expressions which are evaluated and the\nanswer is returned together with its inferred type.  Global type declarations\nand function definitions entered at the command prompt are stored with the\nprogram.  Input may span one or more lines and should end with a period (`.`).\n\n    \u003e 1+2.\n    3 :: num\n\n    \u003e x^2 where x := sin(1)-2.\n    1.3421894790419853 :: num\n\n    \u003e true /\\ false \\/ true.\n    true :: bool\n\n    \u003e \"hello\" // \"world\".\n    \"helloworld\" :: string\n\n    \u003e [1,2,3] ++ [4,5].\n    [1, 2, 3, 4, 5] :: [num]\n\n    \u003e (1+2, \\ true).\n    (3, false) :: (num, bool)\n\n    \u003e x-\u003ex+1.\n    $0-\u003e$0+1 :: num-\u003enum\n\n    \u003e f where f(x) := x+1.\n    $0-\u003e$0+1 :: num-\u003enum\n\n    \u003e map(f, [1,2,3]) where f(x) := x+1.\n    [2,3,4] :: [num]\n\n    \u003e zip([1, 2, 3], [true, true, false]).\n    [(1, true), (2, true), (3, false)] :: [(num, bool)]\n\nType variables and formal arguments of functions and lambdas are internally\nanonymized as Prolog variables and displayed as `$i` where `i` is an integer.\n\nMany functions and operators are predefined in Husky, either as built-ins or\nas prelude functions, see [Functions](#functions).\n\nIn Husky, `v-\u003eb` is a lambda (abstraction) with argument `v` and body\nexpression `b`.  Applications of lambdas are written with a colon (`:`), for\nexample `(v-\u003ev+1):3` applies the lambda `v-\u003ev+1` to the value `3` giving `3+1`\nwhich evaluates to `4`.\n\nWhen applying named functions the usual parenthesized form can be used, for\nexample `f(3)` to apply `f` to the value `3`.  Also `f:3` is permitted.\n\nCurrying of functions and operators is supported.  For example, addition can be\nwritten in functional form `+(x,y)` and `(+):x:y` and even `+(x):y`.  These are\nall different representations of the same expression.  This makes Currying\nintuitive, for example `+(1)` is the increment function, `*(2)` doubles, and\n`/(1)` inverts (note the parameter placement order in this case!).\n\nLists in Husky are written with brackets, for example`[]` and `[1,2]`.  The\nspecial form `[x|xs]` represents a list with head expression `x` and tail\nexpression `xs`.  The dot notation `x.xs` represents a list with head `x` and\ntail `xs`, where `.` is the list constructor function commonly referred to as\n\"cons\".\n\nDefinitions\n-----------\n\nA definition in Husky is written as `LHS := RHS`, where `LHS` is a name or a\nfunction name with parenthesized arguments and `RHS` is an expression.\n\nFor example:\n\n    one := 1.\n    inc(x) := x + 1.\n    hypotenuse(x, y) := sqrt(x^2 + y^2).\n\nDefinitions of functions can span more than one `LHS := RHS` pair in which the\ndefinitions should be separated with a semicolon (`;`).  This allows you to\ndefine pattern arguments for function specialization:\n\n    fact(0) := 1;\n    fact(n) := n * fact(n - 1).\n\nMultiple definitions of a function typically have constants and data structures\nas arguments for which the function has a specialized function body.\n\nA wildcard formal argument `_` is permitted and represents an unused argument.\nFor example to define the `const` combinator function:\n\n    const(x, _) := x.\n\nTypes\n-----\n\nTypes are automatically inferred, like in Haskell.  Types can also be\nexplicitly associated with definitions using the `::` operator.  New named\ntypes and parametric types can be defined.\n\nHusky has three built-in atomic types:\n\n    bool           Booleans\n    num            integers, rationals, and floats\n    string         strings\n\n- the Boolean type has two values `true` and `false`;\n- rationals are written with `rdiv` in the form `numerator rdiv denominator`;\n- floats are written conventionally with +/- sign, a period, and exponent `E`;\n- strings are sequences of characters enclosed in double quotes (`\"`);\n- the `nil` constant denotes the so-called \"bottom value\" (an undefined value)\n  and is polymorphic.\n\nHusky has three built-in type constructors:\n\n    [alpha]          a list of elements of type alpha\n    (alpha, beta)    a tuple is the product of types alpha and beta\n    alpha -\u003e beta    a function mapping elements of type alpha to type beta\n\nNew (parametric) data types and their constructors can be defined as follows:\n\n    MyType :: type.\n    Constructor1(arg1, arg2, ...) :: MyType(parm1, parm2, ...).\n    ...\n    ConstructorN(arg1, arg2, ...) :: MyType(parm1, parm2, ...).\n\nA type variable is a name, except `bool`, `num`, and `string`.  Also symbols\nsuch as `*` and `**` can be used as type variables.  For example, a polymorphic\nlist type can be written as `[alpha]` or `[*]` and the comparison operator `=`\nhas type `(=) :: * -\u003e * -\u003e bool`.\n\nSome examples of named types and parametric types:\n\n    one :: num.\n    one := 1.\n\n    inc :: num-\u003enum.\n    inc := +(1).\n\n    Day :: type.\n    Mon :: Day.\n    Tue :: Day.\n    Wed :: Day.\n    Thu :: Day.\n    Fri :: Day.\n    Sat :: Day.\n    Sun :: Day.\n    WeekDays :: [Day].\n    WeekDays := [Mon,Tue,Wed,Thu,Fri].\n    isWeekDay(Mon) := true;\n    isWeekDay(Tue) := true;\n    isWeekDay(Wed) := true;\n    isWeekDay(Thu) := true;\n    isWeekDay(Fri) := true;\n    isWeekDay(day) := false.\n    verify := all(map(isWeekDay, WeekDays)).\n\n    BinTree :: type.\n    Leaf :: BinTree(*).\n    Node(*, BinTree(*), BinTree(*)) :: BinTree(*).\n\nExamples\n--------\n\n    \u003e a := 2.\n    { DEFINED a::num }\n\n    \u003e f(x) := x+1.\n    { DEFINED f::num-\u003enum }\n\n    \u003e f(a).\n    3 :: num\n\n    % the same function f, using the colon syntax for application:\n    \u003e f:x := (+):x:1.\n    { DEFINED f::num-\u003enum }\n\n    % the same function f, using currying:\n    \u003e f := +(1).\n    { DEFINED f::num-\u003enum }\n\n    % function definitions are translated to lambdas,\n    % typing the function name returns its lambda expression:\n\n    \u003e f(a) := a+1.\n    { DEFINED f::num-\u003enum }\n    \u003e f.\n    $0-\u003e$0+1 :: num-\u003enum\n\n    \u003e plus(x,y) := x+y.\n    { DEFINED plus::num-\u003enum-\u003enum }\n    \u003e plus.\n    $0-\u003e$1-\u003e$0+$1 :: num-\u003enum-\u003enum\n\n    % a lambda can be specialized by multiple abstractions, each\n    % separated by a ';', e.g. the factorial function:\n    \u003e fact(0) := 1;\n      fact(n) := n*fact(n-1).\n    { DEFINED fac::num-\u003enum }\n    \u003e fact.\n    0-\u003e1;$0-\u003e$0*fac($0-1) :: num-\u003enum\n    \u003e fact(6).\n    720 :: num\n\n    % constants can be defined to construct new data types\n\n    % declare a BinTree type:\n    \u003e BinTree :: type.\n    { DECLARED BinTree::type }\n\n    % define a Leaf constant:\n    \u003e Leaf :: BinTree(*).\n    { DECLARED Leaf::BinTree($0) }\n\n    % define a Node constructor:\n    \u003e Node(*, BinTree(*), BinTree(*)) :: BinTree(*).\n    Node(num, BinTree(num), BinTree(num)) :: BinTree(num).\n\n    % try it out by building a small tree (see examples/btree.sky):\n    \u003e Node(3, Node(1, Leaf, Leaf), Node(4, Leaf, Leaf)).\n    Node(3, Node(1, Leaf, Leaf), Node(4, Leaf, Leaf)) :: BinTree(num)\n\nFunctions\n---------\n\nBuilt-in operators and functions are defined in prelude.sky:\n\n    - x         unary minus (can also use 'neg' for unary minus, e.g. for currying)\n    x + y       addition\n    x - y       subtraction\n    x * y       multiplication\n    x / y       division\n    x rdiv y    rational division with \"infinite\" precision\n    x div y     integer division\n    x mod y     modulo\n    x ^ y       power\n    min(x,y)    minimum\n    max(x,y)    maximum\n    x = y       equal (structural comparison)\n    x \u003c\u003e y      not equal (structural comparison)\n    x \u003c y       less (num and string)\n    x \u003c= y      less or equal (num and string)\n    x \u003e y       greater (num and string)\n    x \u003e= y      greater or equal (num and string)\n    \\ x         logical not\n    x /\\ y      logical and\n    x \\/ y      logical or\n    gcd(x, y)   GCD\n    lcm(x, y)   LCM\n    fac(x)      factorial\n    fib(x)      Fibonacci\n    id(x)       identity (returns x)\n    abs(x)      absolute value\n    sin(x)      sine\n    cos(x)      cosine\n    tan(x)      tangent\n    asin(x)     arc sine\n    acos(x)     arc cosine\n    atan(x)     arc tangent\n    exp(x)      e^x\n    log(x)      natural logarithm (nil when x\u003c0)\n    sqrt(x)     root (nil when x\u003c0)\n    x // y      string concatenation\n    (x,y)       tuple (note: tuple constructors are strict, not lazy)\n    x.xs        a list with head x and tail xs (list constructors are lazy)\n    [x|xs]      same as above: a list with head x and tail xs\n    # xs        list length\n    xs ? n      element of list xs at index n \u003e= 1\n    hd(xs)      head element of list xs (nil when x=[])\n    tl(xs)      tail element of list xs (nil when x=[])\n    init(xs)    init of xs without the last element\n    last(xs)    last element of xs\n    xs ++ ys    list concatenation\n    xs \\\\ ys    list subtraction\n    x is_in xs  list membership check (positive test)\n    x not_in xs list membership check (negative test)\n    fst((x,y))  first (=x)\n    snd((x,y))  second (=y)\n    even(x)     true when x is even\n    odd(x)      true when x is odd\n\n    num(s)      convert string s to number (nil when s is not numeric)\n    str(x)      convert number x to string\n    s2ascii(s)  convert string s to list of ASCII codes\n    ascii2s(xs) convert list xs of ASCII codes to string\n\n    read        prompts user and returns input string\n    write(x)    display x (returns x)\n    writeln(x)  display x '\\n' (returns x)\n    getc        returns single input char in ASCII\n    putc(x)     display character of ASCII code x (returns x)\n\n    map         map(f, xs) maps f onto xs [f(x1), f(x2), ..., f(xk)]\n    filter      filter(p, xs) yields elements of xs that fulfill p\n    foldl       foldl(f, x, xs) = f(x, ... f(f(f(x1, x2), x3), x4), ...)\n    foldr       foldr(f, x, xs) = f(... f(xk-2, f(xk-1, f(xk, x))) ... )\n    foldl1      foldl1(f, xs) foldl over non-empty list\n    foldr1      foldr1(f, xs) foldr over non-empty list\n    scanl       scanl left scan\n    scanr       scanr right scan\n    all         all(p, xs) = true if all elements in xs fulfill p\n    any         any(p, xs) = true if any element in xs fulfills p\n    concat      concat list of lists\n    zip         zip(xs, ys) zips two lists\n    unzip       unzip(xys) unzips list of tuples\n    zipwith     zipwith(f, xs, ys) zips two lists using operator f\n    until       until(p, f, x) applies f to x until p(x) holds\n    iterate     iterate(f, x) = [x, f(x), f(f(x)), f(f(f(x))), ... ]\n    from        from(n) = [n, n+1, n+2, ...]\n    repeat      repeat(x) produces [x,x,x,...]\n    replicate   replicate(n, x) generates list of n x's\n    cycle       cycle(xs) generates cyclic list xs ++ xs ++ ...\n    drop        drop(n, xs) drops n elements from xs\n    take        take(n, xs) takes n elements from xs\n    dropwhile   dropwhile(p, xs) drops first elements fulfilling p from xs\n    takewhile   takewhile(p, xs) takes first elements fulfilling p from xs\n    reverse     reverse list\n    f @ g       function composition (\"f after g\")\n    twiddle     twiddle operands twiddle(f:x, y) means f(y, x)\n    flip        flip(f, x, y) flips operands to apply f(y, x)\n    curry       curry function curry(f, x, y) := f((x, y))\n    uncurry     uncurry function uncurry(f, (x, y)) := f(x, y)\n\nLambdas\n-------\n\nA lamba (abstraction) is written as `v-\u003eb`.  A lambda expression may have\nseveral lambda alternatives for function specialization.  Alternatives are\nrepresented by lambdas separated by semicolons (`;`).  Each lambda should\nspecify a specialized argument for selection, such as constants and partial\ndata structures.\n\n    (v-\u003eb)                  a lambda abstraction\n    (v-\u003eb; w-\u003ec)            a lambda with two specializations\n\nArgument `v` and `w` may be a name, a constant, or a constructor with named\nparameters.  The body of a lambda is just an expression.\n\nLambdas should be parenthesized when used in expressions, because the `-\u003e`\nand `;` operators have a low precedence.\n\nFunction application is performed with the conventional syntax of argument\nparameterization:\n\n    \u003e f := (x-\u003ex+1).\n    { DEFINED f::num-\u003enum }\n    \u003e f(3).\n    4 :: num\n    \u003e iszero := (0-\u003etrue; n-\u003efalse).\n    { DEFINED iszero::num-\u003ebool }\n\nHowever, applying a lambda directly to an argument requires the `:` application\noperator:\n\n    \u003e (x-\u003ex+1):3.\n    4 :: num\n\nFunctions and functions with specializations are automatically translated to\nthe corresponding lambda abstractions when stored with the program:\n\n    \u003e f(x) := x+1.\n    { DEFINED f::num-\u003enum }\n    \u003e f.\n    ($0-\u003e$0+1) :: (num-\u003enum)\n    \u003e iszero(0) := true;\n    |: iszero(n) := false.\n    { DEFINED iszero::num-\u003ebool }\n    \u003e iszero.\n    (0-\u003etrue;$0-\u003efalse) :: (num-\u003ebool)\n\nList comprehension\n------------------\n\nA list comprehension has the following form:\n\n    [ expr | qual; qual; ...; qual ]\n\nwhere the qualifiers are generators of the form `x \u003c- xs` or guards over the\nfree variables.  Guards over one or more generator variables must be placed to\nthe right of the generator, i.e. variable scoping applies to the right.\n\nFor example:\n\n    [ x^2 | x \u003c- 1..10; even(x) ].\n\n    [ (a,b) | a \u003c- 1..3; b \u003c- 1..a ].\n\n    [ (a,b) | a \u003c- 1..5; odd(a); b \u003c- 1..a; a mod b = 0 ].\n\nMacros\n------\n\nMacros are defined with `==` to serve as shorthands for expressions and types:\n\n    StartValue   == 0.\n    Address      == num.\n    MachineState == Address-\u003enum.\n    NumTree      == BinTree(num).\n\nMacro expansion is structural: names, functions, lists, and other syntactic\nstructures can be defined as macros with parameters, i.e. macros support\npattern matching for macro specialization.  Parameter names in macros are\nunified (viz.  Prolog unification) to structurally match macro definitions\nwithout evaluating the arguments.\n\nFor example:\n\n    \u003e macro(x, x) == \"equal\".\n    \u003e macro(x, y) == \"not equal\".\n    \u003e macro(1, 1).\n    \"equal\" :: string\n    \u003e macro(1, 2).\n    \"not equal\" :: string\n    \u003e macro(1, 1+0).\n    \"not equal\" :: string\n\nIn fact, list comprehension syntax is entirely implemented by recursive macro\nexpansion into `concat`, `map`, and `filter` (defined in prelude.sky):\n\n    [ f | x \u003c- xs; p; y \u003c- ys; r ] == concat(map(x -\u003e [ f | y \u003c- ys; r ], filter(x -\u003e p, xs))).\n    [ f | x \u003c- xs; p; y \u003c- ys    ] == concat(map(x -\u003e [ f | y \u003c- ys    ], filter(x -\u003e p, xs))).\n\n    [ f | x \u003c- xs; y \u003c- ys; r ] == concat(map(x -\u003e [ f | y \u003c- ys; r ], xs)).\n    [ f | x \u003c- xs; y \u003c- ys    ] == concat(map(x -\u003e [ f | y \u003c- ys    ], xs)).\n\n    [ f | x \u003c- xs; p; q; r ] == [ f | x \u003c- xs; p /\\ q; r ].\n    [ f | x \u003c- xs; p; q    ] == [ f | x \u003c- xs; p /\\ q    ].\n\n    [ x | x \u003c- xs; p ] == filter(x -\u003e p, xs).\n    [ f | x \u003c- xs; p ] == map(x -\u003e f, filter(x -\u003e p, xs)).\n\n    [ x | x \u003c- xs ] == xs.\n    [ f | x \u003c- xs ] == map(x -\u003e f, xs).\n\nThe monad `do` operator (defined in monads.sky) is a macro:\n\n    do(x := y; z) == do(z) where x := y.\n    do(x \u003c- y; z) == y \u003e\u003e= (x -\u003e do(z)).\n    do(z) == z.\n\nMacros do not evaluate arguments!  Only syntactical structures can be pattern\nmatched.  New syntax can be introduced by declaring prefix, infix, and postfix\noperators, see [Commands](#commands).\n\nSpecial constructs\n------------------\n\n    if b then x else y      a conditional expression\n\n    x where y := z          each y in expression x is replaced by expression z\n\n    fix(f)                  the fixed-point Y-combinator (fix:f = f:(fix:f))\n\n    (let defs in val)       the 'let' construct (use parenthesis!)\n\n    case val of (const1 -\u003e val1; const2 -\u003e val2; ... constk -\u003e valk)\n                            the 'case' construct (translated to lambdas)\n\nFor example:\n\n    test := (let f := ^(2); start := 1; end := 10 in map(f, start..end)).\n\n    case n of (1 -\u003e \"one\"; 2 -\u003e \"two\"; 3 -\u003e \"three\")\n\nCommands\n--------\n\n    listing.                lists the contents of the loaded program(s)\n    bye.                    close session\n    reset.                  soft reset\n    trace.                  trace on\n    notrace.                trace off\n    profile.                collect execution profile stats\n    noprofile.              profile off\n    save.                   create standalone Husky executable\n    load \"file\".            load file\n    save \"file\".            save listing to file\n    :- Goal.                execute Prolog Goal\n    remove name.            remove name from definitions\n    prefix (op).            declare prefix operator op\n    postfix (op).           declare postfix operator op\n    infix (op).             declare non-associative infix operator op\n    infixl (op).            declare left associative infix operator op\n    infixr (op).            declare right associative infix operator op\n    num prefix (op).        declare prefix operator op with precedence num\n    num postfix (op).       declare postfix operator op with precedence num\n    num infix (op).         declare non-associative infix operator op w. prec. num\n    num infixl (op).        declare left associative infix operator op w. prec. num\n    num infixr (op).        declare right associative infix operator op w. prec. num\n\nGotchas\n-------\n\n\n- An expression or command must be terminated by a period (`.`).\n\n- There cannot be a space after the list cons dot (`.`) operator in `x.xs`,\n  otherwise the system considers the input line has ended.  As an alternative\n  use `[x|xs]`, for example if `xs` is a compound expression.\n\n- There must be a space between different operators, otherwise the system is\n  not able to separate the operators and parse the expression.  For example:\n\n      x\\/\\y           should be written         x\\/ \\y\n      x+#l            should be written         x+ #l\n      x*-y            should be written         x* -y\n      x\u003c-y            should be written         x\u003c -y\n\n- Parenthesization follows the Prolog syntax, so `f(x,y)` works, but `f(x)(y)`\n  causes a syntax error.  Instead, use colons, e.g. `f:x:y` or a mixed form\n  such as `f(x):y`.\n\n- The scope of the `where` construct is limited to its left-hand side.\n  Therefore, it does not support recursive definitions:\n\n      BAD:\n      ... f(x) ... where f(n) := ... f(n-1) ...\n\n  use the `fix` operator instead:\n\n      GOOD:\n      ... fix(f, x) ... where f(ff, n) := ... ff(n-1) ...\n\n- When using tuples in `where`, be aware that tuple constructors are strict:\n\n      x+1 where (x,y) := (1,write(\"abc\");\n\n  which displays \"abc\" even though `y` is not used, because tuple members are\n  evaluated.\n\n- Patterns in function arguments may consist of (parameterized) constants,\n  empty list `[]`, `x.xs` non-empty list with head `x` and tail `xs`, and\n  tuples `(x,y)`.  These should not be nested:\n\n      BAD:\n      f((x,y).xs) := ...\n\n      GOOD:\n      f(p.xs) := ... where (x,y) := p.\n\n      BAD:\n      f(BinTree(BinTree(left, right), rest)) := ...\n\n      GOOD:\n      f(BinTree(tree, rest)) := ...\n              where left := f(tree)\n              where right := g(tree)\n              where f(BinTree(l, r)) := l\n              where g(BinTree(l, r)) := r.\n\nInstallation\n------------\n\nInstall SWI-Prolog 8.2.1 or greater from \u003chttps://www.swi-prolog.org\u003e.\n\nThen clone Husky with git:\n\n    $ git clone https://github.com/Genivia/husky\n\nRun `swipl` then load Husky and run the `husky` interpreter:\n\n    $ swipl\n    ?- [husky].\n    :- husky.\n\nAfter starting Husky, load any one of the example .sky files with:\n\n    \u003e load \"examples/\u003cfilename\u003e\".\n\nTo delete programs from memory and reset Husky to system defaults:\n\n    \u003e reset.\n\nTo create a stand-alone executable, run `swipl` and enter the following\ncommands:\n\n    $ swipl\n    :- [husky].\n    :- husky.\n    \u003e save.\n    \u003e bye.\n\nThis creates the `Husky' executable with the prelude functions preloaded.  You\ncan also load one or more programs and then save the executable with the\nprograms included.\n\nHusky source files:\n\n    README.md             this file\n    husky.pl              the Husky interpreter\n    prelude.sky           built-in definitions\n\nHusky program examples:\n\n    palindrome.sky        palindrome\n    btree.sky             binary trees\n    clientserver.sky      client-server stream example\n    combinators.sky       SKI combinator calculus\n    cr.sky                Chains of Recurrences algebra\n    hamming.sky           the Hamming problem\n    hangman.sky           a simple hangman game\n    matrix.sky            matrix and vector operations\n    monads.sky            monads a-la Haskell\n    primes.sky            primes\n    qsort.sky             quicksort\n    queens.sky            the queens problem\n    semantics.sky         denotational semantics example\n\nAuthor\n------\n\nHusky is created by Prof. Robert A. van Engelen, engelen@acm.org.\n\nLicense\n-------\n\nOpen source GPL (GNU public license).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgenivia%2Fhusky","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgenivia%2Fhusky","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgenivia%2Fhusky/lists"}