{"id":13474704,"url":"https://github.com/billpmurphy/hask","last_synced_at":"2025-03-26T22:31:08.986Z","repository":{"id":35586876,"uuid":"39859622","full_name":"billpmurphy/hask","owner":"billpmurphy","description":"Haskell language features and standard libraries in pure Python.","archived":false,"fork":false,"pushed_at":"2018-08-08T16:03:05.000Z","size":257,"stargazers_count":865,"open_issues_count":13,"forks_count":35,"subscribers_count":28,"default_branch":"master","last_synced_at":"2025-03-16T09:21:06.936Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/billpmurphy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-28T21:53:38.000Z","updated_at":"2025-02-18T22:22:05.000Z","dependencies_parsed_at":"2022-09-18T00:51:12.696Z","dependency_job_id":null,"html_url":"https://github.com/billpmurphy/hask","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/billpmurphy%2Fhask","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/billpmurphy%2Fhask/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/billpmurphy%2Fhask/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/billpmurphy%2Fhask/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/billpmurphy","download_url":"https://codeload.github.com/billpmurphy/hask/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245747592,"owners_count":20665815,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-07-31T16:01:14.198Z","updated_at":"2025-03-26T22:31:08.490Z","avatar_url":"https://github.com/billpmurphy.png","language":"Python","readme":"# Hask\n\n[![Build Status](https://travis-ci.org/billpmurphy/hask.svg)](https://travis-ci.org/billpmurphy/hask)\n[![Coverage Status](https://coveralls.io/repos/billpmurphy/hask/badge.svg?branch=master\u0026service=github)](https://coveralls.io/github/billpmurphy/hask?branch=master)\n\n\nHask is a pure-Python, zero-dependencies library that mimics most of the core\nlanguage tools from Haskell, including:\n\n* Full Hindley-Milner type system (with typeclasses) that will typecheck any\n  function decorated with a Hask type signature\n* Easy creation of new algebraic data types and new typeclasses, with\n  Haskell-like syntax\n* Pattern matching with `case` expressions\n* Automagical function currying/partial application and function composition\n* Efficient, immutable, lazily evaluated `List` type with Haskell-style list\n  comprehensions\n* All your favorite syntax and control flow tools, including operator sections,\n  monadic error handling, guards, and more\n* Python port of (some of) the standard libraries from Haskell's `base`,\n  including:\n    * Algebraic datatypes from the Haskell `Prelude`, including `Maybe` and\n      `Either`\n    * Typeclasses from the Haskell `base` libraries, including `Functor`,\n      `Applicative`, `Monad`, `Enum`, `Num`, and all the rest\n    * Standard library functions from `base`, including all functions from\n      `Prelude`, `Data.List`, `Data.Maybe`, and more\n\n\nFeatures not yet implemented, but coming soon:\n\n* Python 3 compatibility\n* Better support for polymorphic return values/type defaulting\n* Better support for lazy evaluation (beyond just the `List` type and pattern matching)\n* More of the Haskell standard library (`Control.*` libraries, QuickCheck, and more)\n* Monadic, lazy I/O\n\n**Note that all of this is still very much pre-alpha, and some things may be buggy!**\n\n## Installation\n\n1) `git clone https://github.com/billpmurphy/hask`\n\n2) `python setup.py install`\n\nTo run the tests: `python tests.py`.\n\n\n## Why did you make this?\n\nI wanted to cram as much of Haskell into Python as possible while still being\n100% compatible with the rest of Python, just to see if any useful ideas came\nout of the result. Also, it was fun!\n\nContributions, forks, and extensions to this experiment are always welcome!\nFeel free to submit a pull request, open an issue, or email me. In the spirit\nof this project, abusing the Python language as much as possible is encouraged.\n\n\n## Features\n\nHask is a grab-bag of features that add up to one big pseudo-Haskell functional\nprogramming library. The rest of this README lays out the basics.\n\nI recommend playing around in the REPL while going through the examples. You\n\nTo import all the language features: `from hask import *`\nTo import the Prelude: `from hask import Prelude`\nTo import a `base` library, e.g. `Data.List`: `from hask import Data.List`\n\n\n### The List type and list comprehensions\n\n\nHask provides the `List` type, a lazy and statically-typed list, similar to\nHaskell's standard list type.\n\nTo create a new `List`, just put the elements inside `L[` and `]` brackets, or\nwrap an existing iterable inside `L[ ]`.\n\n```python\n\u003e\u003e\u003e L[1, 2, 3]\nL[1, 2, 3]\n\n\u003e\u003e\u003e my_list = [\"a\", \"b\", \"c\"]\n\u003e\u003e\u003e L[my_list]\nL['a', 'b', 'c']\n\n\u003e\u003e\u003e L[(x**2 for x in range(1, 11))]\nL[1 ... ]\n```\n\nTo add elements to the front of a List, use `^`, the cons operator.  To combine\ntwo lists, use `+`, the concatenation operator.\n\n```python\n\u003e\u003e\u003e 1 ^ L[2, 3]\nL[1, 2, 3]\n\n\u003e\u003e\u003e \"goodnight\" ^ (\"sweet\" ^ (\"prince\" ^ L[[]]))\nL[\"goodnight\", \"sweet\", \"prince\"]\n\n\u003e\u003e\u003e \"a\" ^ L[1.0, 10.3]  # type error\n\n\u003e\u003e\u003e L[1, 2] + L[3, 4]\nL[1, 2, 3, 4]\n```\n\nLists are always evaluated lazily, and will only evaluate list elements as\nneeded, so you can use infinite Lists or put never-ending generators inside of\na `List`. (Of course, you can still blow up the interpreter if you try to\nevaluate the entirety of an infinite List, e.g. by trying to find the length of\nthe List with `len`.)\n\nOne way to create infinite lists is via list comprehensions. As in Haskell,\nthere are four basic type of list comprehensions:\n\n\n```python\n# list from 1 to infinity, counting by ones\nL[1, ...]\n\n# list from 1 to infinity, counting by twos\nL[1, 3, ...]\n\n# list from 1 to 20 (inclusive), counting by ones\nL[1, ..., 20]\n\n# list from 1 to 20 (inclusive), counting by fours\nL[1, 5, ..., 20]\n```\n\nList comprehensions can be used on ints, longs, floats, one-character strings,\nor any other instance of the `Enum` typeclass (more on this later).\n\nHask provides all of the Haskell functions for List manipulation (`take`,\n`drop`, `takeWhile`, etc.), or you can also use Python-style indexing.\n\n\n```python\n\u003e\u003e\u003e L[1, ...]\nL[1 ...]\n\n\n\u003e\u003e\u003e from hask.Data.List import take\n\u003e\u003e\u003e take(5, L[\"a\", \"b\", ...])\nL['a', 'b', 'c', 'd', 'e']\n\n\n\u003e\u003e\u003e L[1,...][5:10]\nL[6, 7, 8, 9, 10]\n\n\n\u003e\u003e\u003e from hask.Data.List import map\n\u003e\u003e\u003e from hask.Data.Char import chr\n\u003e\u003e\u003e letters = map(chr, L[97, ...])\n\u003e\u003e\u003e letters[:9]\nL['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']\n\n\n\u003e\u003e\u003e len(L[1, 3, ...])  # uh oh\n```\n\nOtherwise, you can use `List` just like you would use a regular Python list.\n\n```python\nfor i in L[0, ...]:\n    print i\n\n\n\u003e\u003e\u003e 55 in L[1, 3, ...]\nTrue\n```\n\n### Algebraic Data Types\n\nHask allows you to define [algebraic\ndatatypes](https://wiki.haskell.org/Algebraic_data_type), which are immutable\nobjects with a fixed number of typed, unnamed fields.\n\nHere is the definition for the infamous `Maybe` type:\n\n```python\nfrom hask import data, d, deriving\nfrom hask import Read, Show, Eq, Ord\n\nMaybe, Nothing, Just =\\\n    data.Maybe(\"a\") == d.Nothing | d.Just(\"a\") \u0026 deriving(Read, Show, Eq, Ord)\n```\n\nLet's break this down a bit. The syntax for defining a new [type\nconstructor](https://wiki.haskell.org/Constructor#Type_constructor) is:\n\n```python\ndata.TypeName(\"type param\", \"type param 2\" ... \"type param n\")\n```\n\nThis defines a new algebraic datatype with type parameters.\n\nTo define [data\nconstructors](https://wiki.haskell.org/Constructor#Data_constructor) for this\ntype, use `d.` The name of the data constructor goes first, followed by its\nfields. Multiple data constructors should be separted by `|`. If your data\nconstructor has no fields, you can omit the parens. For example:\n\n```python\nFooBar, Foo, Bar =\\\n    data.FooBar(\"a\", \"b\") == d.Foo(\"a\", \"b\", str) | d.Bar\n```\n\nTo automagically derive typeclass instances for the type, add `\u0026\nderiving(...typeclasses...)` after the data constructor declarations.\nCurrently, the only typeclasses that can be derived are `Eq`, `Show`, `Read`,\n`Ord`, and `Bounded`.\n\nPutting it all together, here are the definitions of `Either` and `Ordering`:\n\n```python\nEither, Left, Right =\\\n    data.Either(\"a\", \"b\") == d.Left(\"a\") | d.Right(\"b\") \u0026 deriving(Read, Show, Eq)\n\n\nOrdering, LT, EQ, GT =\\\n    data.Ordering == d.LT | d.EQ | d.GT \u0026 deriving(Read, Show, Eq, Ord, Bounded)\n```\n\nYou can now use the data constructors defined in a `data` statement to create\ninstances of these new types. If the data constructor takes no arguments, you\ncan use it just like a variable.\n\n```python\n\u003e\u003e\u003e Just(10)\nJust(10)\n\n\u003e\u003e\u003e Nothing\nNothing\n\n\u003e\u003e\u003e Just(Just(10))\nJust(Just(10))\n\n\u003e\u003e\u003e Left(1)\nLeft(1)\n\n\u003e\u003e\u003e Foo(1, 2, \"hello\")\nFoo(1, 2, 'hello')\n```\n\nYou can view the type of an object with `_t` (equivalent to `:t` in ghci).\n\n```python\n\u003e\u003e\u003e from hask import _t\n\n\u003e\u003e\u003e _t(1)\nint\n\n\u003e\u003e\u003e _t(Just(\"soylent green\"))\n(Maybe str)\n\n\u003e\u003e\u003e _t(Right((\"a\", 1)))\n(Either a (str, int))\n\n\u003e\u003e\u003e _t(Just)\n(a -\u003e Maybe a)\n\n\u003e\u003e\u003e _t(L[1, 2, 3, 4])\n[int]\n```\n\n\n### The type system and typed functions\n\nSo what's up with those types? Hask operates its own shadow [Hindley-Milner\ntype system](https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system)\non top of Python's type system; `_t` shows the Hask type of a particular\nobject.\n\nIn Hask, typed functions take the form of `TypedFunc` objects, which are typed\nwrappers around Python functions. There are two ways to create `TypedFunc`\nobjects:\n\n1) Use the `sig` decorator to decorate the function with the type signature\n\n```python\n@sig(H/ \"a\" \u003e\u003e \"b\" \u003e\u003e \"a\")\ndef const(x, y):\n    return x\n```\n\n2) Use the `**` operator (similar to `::` in Haskell) to provide the type.\nUseful for turning functions or lambdas into `TypedFunc` objects in the REPL,\nor wrapping already-defined Python functions.\n\n```python\ndef const(x, y):\n    return x\n\nconst = const ** (H/ \"a\" \u003e\u003e \"b\" \u003e\u003e \"a\")\n```\n\n`TypedFunc` objects have several special properties.  First, they are type\nchecked--when arguments are supplied, the type inference engine will check\nwhether their types match the type signature, and raise a `TypeError` if there\nis a discrepancy.\n\n```python\n\u003e\u003e\u003e f = (lambda x, y: x + y) ** (H/ int \u003e\u003e int \u003e\u003e int)\n\n\u003e\u003e\u003e f(2, 3)\n5\n\n\u003e\u003e\u003e f(9, 1.0)  # type error\n```\n\nSecond, `TypedFunc` objects can be [partially\napplied](https://wiki.haskell.org/Partial_application):\n\n```python\n\u003e\u003e\u003e g = (lambda a, b, c: a / (b + c)) ** (H/ int \u003e\u003e int \u003e\u003e int \u003e\u003e int)\n\n\u003e\u003e\u003e g(10, 2, 3)\n2\n\n\u003e\u003e\u003e part_g = g(12)\n\n\u003e\u003e\u003e part_g(2, 2)\n3\n\n\u003e\u003e\u003e g(20, 1)(4)\n4\n```\n\n`TypedFunc` objects also have two special infix operators, the `*` and `%`\noperators. `*` is the compose operator (equivalent to `(.)` in Haskell), so\n`f * g` is equivalent to `lambda x: f(g(x))`. `%` is just the apply operator,\nwhich applies a `TypedFunc` to one argument (equivalent to `($)` in Haskell).\nThe convinience of this notation (when combined with partial application)\ncannot be overstated--you can get rid of a ton of nested parenthesis this way.\n\n```python\n\u003e\u003e\u003e from hask.Prelude import flip\n\u003e\u003e\u003e h = (lambda x, y: x / y) ** (H/ float \u003e\u003e float \u003e\u003e float)\n\n\u003e\u003e\u003e h(3.0) * h(6.0) * flip(h, 2.0) % 36.0\n9.0\n```\n\nThe compose operation is also typed-checked, which makes it appealing to write\nprograms in [pointfree style](https://wiki.haskell.org/Pointfree), i.e,\nchaining together lots of functions with composition and relying on the type\nsystem to catch programming errors.\n\nAs you would expect, data constructors are also just `TypedFunc` objects:\n\n```python\n\u003e\u003e\u003e Just * Just * Just * Just % 77\nJust(Just(Just(Just(77))))\n```\n\nThe type signature syntax is very simple, and consists of a few basic\nprimitives that can be combined to build any type signature:\n\n| Primitive | Syntax/examples |\n| --------- | --------------- |\n| Type literal for Python builtin type or user-defined class | `int`, `float`, `set`, `list` |\n| Type variable | `\"a\"`, `\"b\"`, `\"zz\"` |\n| `List` of some type | `[int]`, `[\"a\"]`, `[[\"a\"]]` |\n| Tuple type | `(int, int)`, `(\"a\", \"b\", \"c\")`, `(int, (\"a\", \"b\"))` |\n| ADT with type parameters | `t(Maybe, \"a\")`, `t(Either, \"a\", str)` |\n| Unit type (`None`) | `None` |\n| Untyped Python function | `func` |\n| Typeclass constraint | `H[(Eq, \"a\"), (Show, \"b\")]/`, `H[(Functor, \"f\"), (Show, \"f\")]/` |\n\nSome examples:\n\n```python\n# add two ints together\n@sig(H/ int \u003e\u003e int \u003e\u003e int)\ndef add(x, y):\n    return x + y\n\n\n# reverse order of arguments to a function\n@sig(H/ (H/ \"a\" \u003e\u003e \"b\" \u003e\u003e \"c\") \u003e\u003e \"b\" \u003e\u003e \"a\" \u003e\u003e \"c\")\ndef flip(f, b, a):\n    return f(a, b)\n\n\n# map a Python (untyped) function over a Python (untyped) set\n@sig(H/ func \u003e\u003e set \u003e\u003e set)\ndef set_map(fn, lst):\n    return set((fn(x) for x in lst))\n\n\n# map a typed function over a List\n@sig(H/ (H/ \"a\" \u003e\u003e \"b\") \u003e\u003e [\"a\"] \u003e\u003e [\"b\"])\ndef map(f, xs):\n    return L[(f(x) for x in xs)]\n\n\n# type signature with an Eq constraint\n@sig(H[(Eq, \"a\")]/ \"a\" \u003e\u003e [\"a\"] \u003e\u003e bool)\ndef not_in(y, xs):\n    return not any((x == y for x in xs))\n\n\n# type signature with a type constructor (Maybe) that has type arguments\n@sig(H/ int \u003e\u003e int \u003e\u003e t(Maybe, int))\ndef safe_div(x, y):\n    return Nothing if y == 0 else Just(x/y)\n\n\n# type signature for a function that returns nothing\n@sig(H/ int \u003e\u003e None)\ndef launch_missiles(num_missiles):\n    print \"Launching {0} missiles! Bombs away!\" % num_missiles\n```\n\nIt is also possible to create type synonyms using `t`. For example, check out the definition of `Rational`:\n\n```python\nRatio, R =\\\n        data.Ratio(\"a\") == d.R(\"a\", \"a\") \u0026 deriving(Eq)\n\n\nRational = t(Ratio, int)\n\n\n@sig(H/ Rational \u003e\u003e Rational \u003e\u003e Rational)\ndef addRational(rat1, rat2):\n    ...\n```\n\n### Pattern matching\n\nPattern matching is a more powerful control flow tool than the `if` statement,\nand can be used to deconstruct iterables and ADTs and bind values to local\nvariables.\n\nPattern matching expressions follow this syntax:\n\n```python\n~(caseof(value_to_match)\n    | m(pattern_1) \u003e\u003e return_value_1\n    | m(pattern_2) \u003e\u003e return_value_2\n    | m(pattern_3) \u003e\u003e return_value_3)\n```\n\nHere is a function that uses pattern matching to compute the fibonacci\nsequence. Note that within a pattern match expression, `m.*` is used to bind\nvariables, and `p.*` is used to access them.\n\n```python\ndef fib(x):\n    return ~(caseof(x)\n                | m(0)   \u003e\u003e 1\n                | m(1)   \u003e\u003e 1\n                | m(m.n) \u003e\u003e fib(p.n - 1) + fib(p.n - 2))\n\n\u003e\u003e\u003e fib(1)\n1\n\n\u003e\u003e\u003e fib(6)\n13\n```\n\nAs the above example shows, you can combine pattern matching and recursive\nfunctions without a hitch.\n\nYou can also deconstruct an iterable using `^` (the cons operator). The variable\nbefore the `^` is bound to the first element of the iterable, and the variable\nafter the `^` is bound to the rest of the iterable. Here is a function that\nadds the first two elements of any iterable, returning `Nothing` if there are\nless than two elements:\n\n```python\n@sig(H[(Num, \"a\")]/ [\"a\"] \u003e\u003e t(Maybe, \"a\"))\ndef add_first_two(xs):\n    return ~(caseof(xs)\n                | m(m.x ^ (m.y ^ m.z)) \u003e\u003e Just(p.x + p.y)\n                | m(m.x)               \u003e\u003e Nothing)\n\n\n\u003e\u003e\u003e add_first_two(L[1, 2, 3, 4, 5])\nJust(3)\n\n\u003e\u003e\u003e add_first_two(L[9.0])\nNothing\n```\n\nPattern matching is also very useful for deconstructing ADTs and assigning\ntheir fields to temporary variables.\n\n```python\ndef default_to_zero(x):\n    return ~(caseof(x)\n                | m(Just(m.x)) \u003e\u003e p.x\n                | m(Nothing)   \u003e\u003e 0)\n\n\n\u003e\u003e\u003e default_to_zero(Just(27))\n27\n\n\n\u003e\u003e\u003e default_to_zero(Nothing)\n0\n```\n\nIf you find pattern matching on ADTs too cumbersome, you can also use numeric\nindexing on ADT fields. An `IndexError` will be thrown if you mess something\nup.\n\n```python\n\u003e\u003e\u003e Just(20.0)[0]\n20.0\n\n\u003e\u003e\u003e Left(\"words words words words\")[0]\n'words words words words'\n\n\u003e\u003e\u003e Nothing[0]  # IndexError\n```\n\n\n### Typeclasses and typeclass instances\n\n\n[Typeclasses](https://en.wikipedia.org/wiki/Type_class) allow you to add\nadditional functionality to your ADTs. Hask implements all of the major\ntypeclasses from Haskell (see the Appendix for a full list) and provides syntax\nfor creating new typeclass instances.\n\nAs an example, let's add a [`Monad`](https://wiki.haskell.org/Monad) instance\nfor the `Maybe` type.  First, however, `Maybe` needs\n[`Functor`](https://wiki.haskell.org/Functor) and\n[`Applicative`](https://wiki.haskell.org/Applicative_functor) instances.\n\n\n```python\ndef maybe_fmap(fn, x):\n    \"\"\"Apply a function to the value inside of a (Maybe a) value\"\"\"\n    return ~(caseof(x)\n                | m(Nothing)   \u003e\u003e Nothing\n                | m(Just(m.x)) \u003e\u003e Just(fn(p.x)))\n\n\ninstance(Functor, Maybe).where(\n    fmap = maybe_fmap\n)\n```\n\n`Maybe` is now an instance of `Functor`. This allows us to call `fmap`\nand map any function of type `a -\u003e b` into a value of type `Maybe a`.\n\n```python\n\u003e\u003e\u003e times2 = (lambda x: x * 2) ** (H/ int \u003e\u003e int)\n\u003e\u003e\u003e toFloat = float ** (H/ int \u003e\u003e float)\n\n\u003e\u003e\u003e fmap(toFloat, Just(10))\nJust(10.0)\n\n\u003e\u003e\u003e fmap(toFloat, fmap(times2, Just(25)))\nJust(50.0)\n```\n\nLots of nested calls to `fmap` get unwieldy very fast. Fortunately, any\ninstance of `Functor` can be used with the infix `fmap` operator, `*`. This is\nequivalent to `\u003c$\u003e` in Haskell. Rewriting our example from above:\n\n```python\n\u003e\u003e\u003e (toFloat * times2) * Just(25)\nJust(50.0)\n\n\u003e\u003e\u003e (toFloat * times2) * Nothing\nNothing\n```\n\nNote that this example uses `*` as both the function compose operator and as\n`fmap`, to lift functions into a `Maybe` value. If this seems confusing,\nremember that `fmap` for functions is just function composition!\n\n\nNow that `Maybe` is an instance of `Functor`, we can make it an instance of\n`Applicative` and then an instance of `Monad` by defining the appropriate\nfunction implementations. To implement `Applicative`, we just need to provide\n`pure`. To implement `Monad`, we need to provide `bind`.\n\n\n```python\nfrom hask import Applicative, Monad\n\ninstance(Applicative, Maybe).where(\n    pure = Just\n)\n\ninstance(Monad, Maybe).where(\n    bind = lambda x, f: ~(caseof(x)\n                            | m(Just(m.a)) \u003e\u003e f(p.a)\n                            | m(Nothing)   \u003e\u003e Nothing)\n)\n```\n\nThe `bind` function also has an infix form, which is `\u003e\u003e` in Hask.\n\n```python\n@sig(H/ int \u003e\u003e int \u003e\u003e t(Maybe, int))\ndef safe_div(x, y):\n    return Nothing if y == 0 else Just(x/y)\n\n\n\u003e\u003e\u003e from hask.Prelude import flip\n\u003e\u003e\u003e divBy = flip(safe_div)\n\n\n\u003e\u003e\u003e Just(9) \u003e\u003e divBy(3)\nJust(3)\n\n\n\u003e\u003e\u003e Just(12) \u003e\u003e divBy(2) \u003e\u003e divBy(2) \u003e\u003e divBy(3)\nJust(1)\n\n\n\u003e\u003e\u003e Just(12) \u003e\u003e divBy(0) \u003e\u003e divBy(6)\nNothing\n```\n\nAs in Haskell, `List` is also a monad, and `bind` for the `List` type is just\n`concatMap`.\n\n```python\n\u003e\u003e\u003e from hask.Data.List import replicate\n\u003e\u003e\u003e L[1, 2] \u003e\u003e replicate(2) \u003e\u003e replicate(2)\nL[1, 1, 1, 1, 2, 2, 2, 2]\n```\n\nYou can also define typeclass instances for classes that are not ADTs:\n\n```python\nclass Person(object):\n    def __init__(self, name, age):\n        self.name = name\n        self.age = age\n\n\ninstance(Eq, Person).where(\n    eq = lambda p1, p2: p1.name == p2.name and p1.age == p2.age\n)\n\n\u003e\u003e\u003e Person(\"Philip Wadler\", 59) == Person(\"Simon Peyton Jones\", 57)\nFalse\n```\n\nIf you want instances of the `Show`, `Eq`, `Read`, `Ord`, and `Bounded`\ntypeclasses for your ADTs, it is adviseable to use `deriving` to automagically\ngenerate instances rather than defining them manually.\n\n\nDefining your own typeclasses is pretty easy--take a look at `help(Typeclass)`\nand look at the typeclasses defined in `Data.Functor` and `Data.Num` to\nsee how it's done.\n\n\n### Operator sections\n\nHask also supports operator sections (e.g. `(1+)` in Haskell). Sections are\njust `TypedFunc` objects, so they are automagically curried and typechecked.\n\n```python\n\u003e\u003e\u003e from hask import __\n\n\u003e\u003e\u003e f = (__ - 20) * (2 ** __) * (__ + 3)\n\u003e\u003e\u003e f(10)\n8172\n\n\u003e\u003e\u003e ((90/__) * (10+__)) * Just(20)\nJust(3)\n\n\u003e\u003e\u003e from hask.Data.List import takeWhile\n\u003e\u003e\u003e takeWhile(__\u003c5, L[1, ...])\nL[1, 2, 3, 4]\n\n\u003e\u003e\u003e (__+__)('Hello ', 'world')\n'Hello world'\n\n\u003e\u003e\u003e (__**__)(2)(10)\n1024\n\n\u003e\u003e\u003e from hask.Data.List import zipWith, take\n\u003e\u003e\u003e take(5) % zipWith(__ * __, L[1, ...], L[1, ...])\nL[1, 4, 9, 16, 25]\n```\n\nAs you can see, this much easier than using `lambda` and adding a type\nsignature with the `(lambda x: ...) ** (H/ ...)` syntax.\n\nIn addition, the types of the `TypedFuncs` created by sections are always\npolymorphic, to allow for any operator overloading.\n\nNote that if you are using IPython, Hask's `__` will conflict with IPython's\nspecial double underscore variable. To avoid conflicts, you can use `from hask\nimport __ as _s` in IPython.\n\n\n### Guards\n\nIf you don't need the full power of pattern matching and just want a neater\nswitch statement, you can use guards. The syntax for guards is almost identical\nto the syntax for pattern matching.\n\n```python\n~(guard(expr_to_test)\n    | c(test_1) \u003e\u003e return_value_1\n    | c(test_2) \u003e\u003e return_value_2\n    | otherwise \u003e\u003e return_value_3\n)\n```\n\nAs in Haskell, `otherwise` will always evaluate to `True` and can be used as a\ncatch-all in guard expressions. If no match is found (and an `otherwise` clause\nis not present), a `NoGuardMatchException` will be raised.\n\nGuards will also play nicely with sections:\n\n```python\n\u003e\u003e\u003e from hask import guard, c, otherwise\n\n\u003e\u003e\u003e porridge_tempurature = 80\n\n\u003e\u003e\u003e ~(guard(porridge_tempurature)\n...     | c(__ \u003c 20)  \u003e\u003e \"Porridge is too cold!\"\n...     | c(__ \u003c 90)  \u003e\u003e \"Porridge is just right!\"\n...     | c(__ \u003c 150) \u003e\u003e \"Porridge is too hot!\"\n...     | otherwise   \u003e\u003e \"Porridge has gone thermonuclear\"\n... )\n'Porridge is just right!'\n```\n\nIf you need a more complex conditional, you can always use lambdas, regular\nPython functions, or any other callable in your guard condition.\n\n```python\ndef examine_password_security(password):\n    analysis = ~(guard(password)\n        | c(lambda x: len(x) \u003e 20) \u003e\u003e \"Wow, that's one secure password\"\n        | c(lambda x: len(x) \u003c 5)  \u003e\u003e \"You made Bruce Schneier cry\"\n        | c(__ == \"12345\")         \u003e\u003e \"Same combination as my luggage!\"\n        | otherwise                \u003e\u003e \"Hope it's not 'password'\"\n    )\n    return analysis\n\n\n\u003e\u003e\u003e nuclear_launch_code = \"12345\"\n\n\u003e\u003e\u003e examine_password_security(nuclear_launch_code)\n'Same combination as my luggage!'\n```\n\n### Monadic error handling (of Python functions)\n\nIf you want to use `Maybe` and `Either` to handle errors raised by Python\nfunctions defined outside Hask, you can use the decorators `in_maybe` and\n`in_either` to create functions that call the original function and return the\nresult wrapped inside a `Maybe` or `Either` value.\n\nIf a function wrapped in `in_maybe` raises an exception, the wrapped function\nwill return `Nothing`. Otherwise, the result will be returned wrapped in a\n`Just`.\n\n```python\ndef eat_cheese(cheese):\n    if cheese \u003c= 0:\n        raise ValueError(\"Out of cheese error\")\n    return cheese - 1\n\nmaybe_eat = in_maybe(eat_cheese)\n\n\u003e\u003e\u003e maybe_eat(1)\nJust(0)\n\n\u003e\u003e\u003e maybe_eat(0)\nNothing\n```\n\nNote that this is equivalent to lifting the original function into the Maybe\nmonad. That is, its type has changed from `func` to `a -\u003e Maybe b`.  This\nmakes it easier to use the convineient monad error handling style commonly seen\nin Haskell with existing Python functions.\n\nContinuing with this silly example, let's try to eat three pieces of cheese,\nreturning `Nothing` if the attempt was unsuccessful:\n\n```python\n\u003e\u003e\u003e cheese = 10\n\u003e\u003e\u003e cheese_left = Just(cheese) \u003e\u003e maybe_eat \u003e\u003e maybe_eat \u003e\u003e maybe_eat\n\u003e\u003e\u003e cheese_left\nJust(7)\n\n\u003e\u003e\u003e cheese = 1\n\u003e\u003e\u003e cheese_left = Just(cheese) \u003e\u003e maybe_eat \u003e\u003e maybe_eat \u003e\u003e maybe_eat\n\u003e\u003e\u003e cheese_left\nNothing\n```\n\nNotice that we have taken a regular Python function that throws Exceptions, and\nare now handling it in a type-safe, monadic way.\n\nThe `in_either` function works just like `in_maybe`. If an Exception is thrown,\nthe wrapped function will return the exception wrapped in `Left`. Otherwise,\nthe result will be returned wrapped in `Right`.\n\n```python\neither_eat = in_either(eat_cheese)\n\n\u003e\u003e\u003e either_eat(Right(10))\nRight(9)\n\n\u003e\u003e\u003e either_eat(Right(0))\nLeft(ValueError('Out of cheese error',))\n```\n\nChained cheese-eating in the `Either` monad is left as an exercise for\nthe reader.\n\nYou can also use `in_maybe` or `in_either` as decorators:\n\n```python\n@in_maybe\ndef some_function(x, y):\n    ...\n```\n\n\n### Standard libraries\n\nAll of your favorite functions from `Prelude`, `Data.List`, `Data.Maybe`,\n`Data.Either`, `Data.Monoid`, and more are implemented too. Everything is\npretty well documented, so if you're not sure about some function or typeclass,\nuse `help` liberally. See the Appendix below for a full list of modules. Some\nhighlights:\n\n```python\n\u003e\u003e\u003e from hask.Data.Maybe import mapMaybe\n\u003e\u003e\u003e mapMaybe(safe_div(12)) % L[0, 1, 3, 0, 6]\nL[12, 4, 2]\n\n\n\u003e\u003e\u003e from hask.Data.List import isInfixOf\n\u003e\u003e\u003e isInfixOf(L[2, 8], L[1, 4, 6, 2, 8, 3, 7])\nTrue\n\n\n\u003e\u003e\u003e from hask.Control.Monad import join\n\u003e\u003e\u003e join(Just(Just(1)))\nJust(1)\n```\n\nHask also provies `TypeFunc` wrappers for everything in `__builtins__` for ease\nof compatibity. (Eventually, Hask will have typed wrappers for most of the\nPython standard library.)\n\n```python\n\u003e\u003e\u003e from hask.Prelude import flip\n\u003e\u003e\u003e from hask.Data.Tuple import snd\n\u003e\u003e\u003e from hask.Python.builtins import divmod, hex\n\n\u003e\u003e\u003e hexMod = hex * snd * flip(divmod, 16)\n\u003e\u003e\u003e hexMod(24)\n'0x8'\n```\n\n### Internals\n\nIf you want to poke around behind the curtain, here are some useful starting\npoints:\n\n* `typeof(obj)` returns an object's type in Hask's type system\n* `has_instance(some_type, typeclass)` tests for typeclass membership\n* `nt_to_tuple` converts instances of `namedtuple` (including Hask ADTs) into\n  regular tuples\n* `typify` converts a Python function into a `TypedFunc` object\n\n\n\n\n\n\n\n## Appendix\n\n**Table 1.** Overview of Hask typeclasses.\n\n| Typeclass | Superclasses | Required functions | Optional functions | Magic Methods |\n| --------- | ------------ | ------------------ | ------------------ | ------------- |\n| `Show` | | `show` | | `str` |\n| `Read` | | `read` | | |\n| `Eq` | | `eq` | `ne` | `==`, `!=` | |\n| `Ord` | `Eq` | `lt` | `gt`, `le`, `ge` | `\u003c`, `\u003c`, `=\u003c`, `=\u003e` | |\n| `Enum` | | `toEnum`, `fromEnum` | `pred`, `succ`, `enumTo`, `enumFromTo`, `enumFromThen`, `enumFromThenTo` | |\n| `Bounded` | | `minBound`, `maxBound` | | |\n| `Functor` | | `fmap` | | `*` |\n| `Applicative` | `Functor` | `pure` | | |\n| `Monad` | `Applicative` | `bind` | | `\u003e\u003e` |\n| `Monoid` | | `mappend`, `mempty` |  `mconcat` | `+` |\n| `Foldable` | | `foldr` | `foldr_`, `foldl`, `foldl_`, `foldr1`, `foldl1`, `toList`, `null`, `length`, `elem`, `maximum`, `minimum`, `sum`, `product` | `len`, `iter` |\n| `Traversable` | `Foldable`, `Functor` | `traverse` | `sequenceA`, `mapM`, `sequence` | |\n| `Num` | `Show`, `Eq` | `add`, `mul`, `abs`, `signum`, `fromInteger`, `negate` | `sub` | `+`, `-`, `*` |\n| `Real` | `Num`, `Ord` | `toRational` | |\n| `Integral` | `Real`, `Enum` | `quotRem`, `divMod`, `toInteger` | `quot`, `rem`, `div`, `mod` | `/`, `%` |\n| `Fractional` | `Num` | `fromRational`, `div` | `recip` | `/` |\n| `Floating` | `Fractional` | `exp`, `sqrt`, `log`, `pow`, `logBase`, `sin`, `tan`, `cos`, `asin`, `atan`, `acos`, `sinh`, `tanh`, `cosh`, `asinh`, `atanh`, `acosh` | |\n| `RealFrac` | `Real`, `Fractional` | `properFraction`, `truncate`, `round`, `ceiling`, `floor` |\n| `RealFloat` | `Floating`, `RealFrac` | `floatRange`, `isNaN`, `isInfinite`, `isNegativeZero`, `atan2` |\n\n\n**Table 2.** Hask library structure.\n\n| Module | Dependencies | Exported functions |\n| ------ | ------------ | ------------------ |\n| `hask` | `hask.lang`, `hask.Data.Char`, `hask.Data.Either`, `hask.Data.Eq`, `hask.Data.Foldable`, `hask.Data.Functor`, `hask.Data.List`, `hask.Data.Maybe`, `hask.Data.Monoid`, `hask.Data.Num`, `hask.Data.Ord`, `hask.Data.Ratio`, `hask.Data.String`, `hask.Data.Traversable`, `hask.Data.Tuple`, `hask.Control.Applicative`, `hask.Control.Monad`, `hask.Python.builtins` | `instance`, `__`, `guard`, `c`, `otherwise`, `NoGuardMatchException`, `L`, `data`, `d`, `deriving`, `sig`, `H`, `t`, `func`, `TypeSignatureError`, `caseof`, `p`, `m`, `IncompletePatternError`, `_t`, `_i`, `_q`, `typeof`, `has_instance`, `Typeclass`, `Hask`, `Read`, `Show`, `Eq`, `Ord`, `Enum`, `Bounded`, `Num`, `Real`, `Integral`, `Fractional`, `Floating`, `RealFrac`, `RealFloat`, `Functor`, `Applicative`, `Monad`, `Traversable`, `Foldable`, `Maybe`, `Just`, `Nothing`, `in_maybe`, `Either`, `Left`, `Right`, `in_either`, `Ordering`, `LT`, `EQ`, `GT` |\n| `hask.Prelude` | `hask.lang` | `hask.Data.Either`, `hask.Data.Eq`, `hask.Data.Foldable`, `hask.Data.Functor`, `hask.Data.List`, `hask.Data.Maybe`, `hask.Data.Num`, `hask.Data.Ord`, `hask.Data.Traversable`, `hask.Data.Tuple`, `hask.Control.Applicative`, `hask.Control.Monad` | `Maybe`, `Just`, `Nothing`, `in_maybe`, `maybe`, `Either`, `Left`, `Right`, `in_either`, `either`, `Ordering`, `LT`, `EQ`, `GT`, `fst`, `snd`, `curry`, `uncurry`, `Read`, `Show`, `show`, `Eq`, `Ord`, `max`, `min`, `compare`, `Enum`, `fromEnum`, `succ`, `pred`, `enumFromThen`, `enumFrom`, `enumFromThenTo`, `enumFromTo`, `Bounded`, `Functor`, `fmap`, `Applicative`, `Monad`, `Foldable`, `Traversable`, `Num`, `abs`, `negate`, `signum`, `Fractional`, `recip`, `Integral`, `toRatio`, `Ratio`, `R`, `Rational`, `Floating`, `exp`, `sqrt`, `log`, `pow`, `logBase`, `sin`, `tan`, `cos`, `asin`, `atan`, `acos`, `sinh`, `tanh`, `cosh`, `asinh`, `atanh`, `acosh`, `Real`, `toRational`, `RealFrac`, `properFraction`, `truncate`, `round`, `ceiling`, `floor`, `RealFloat`, `isNaN`, `isInfinite`, `isNegativeZero`, `atan2`, `subtract`, `even`, `odd`, `gcd`, `lcm`, `Functor`, `Applicative`, `Monad`, `sequence`, `sequence_`, `mapM`, `mapM_`, `id`, `const`, `flip`, `until`, `asTypeOf`, `error`, `undefined`, `map`, `filter`, `head`, `last`, `tail`, `init`, `null`, `reverse`, `length`, `foldl`, `foldl1`, `foldr`, `foldr1`, `and_`, `or_`, `any`, `all`, `sum`, `product`, `concat`, `concatMap`, `maximum`, `minimum`, `scanl`, `scanl1`, `scanr`, `scanr1`, `iterate`, `repeat`, `replicate`, `cycle`, `take`, `drop`, `splitAt`, `takeWhile`, `dropWhile`, `span`, `break_`, `elem`, `notElem`, `lookup`, `zip`, `zip3`, `zipWith`, `zipWith3`, `unzip`, `unzip3`, `lines`, `words`, `unlines`, `unwords` |\n| `hask.Data.Maybe` | `hask.lang`, `hask.Data.Eq`, `hask.Data.Ord`, `hask.Data.Functor`, `hask.Control.Applicative`, `hask.Control.Monad` | `Maybe` (`Nothing`, `Just`), `in_maybe`, `maybe`, `isJust`, `isNothing`, `fromJust`, `listToMaybe`, `maybeToList`, `catMaybes`, `mapMaybe` |\n| `hask.Data.Either` | `hask.lang`, `hask.Data.Eq`, `hask.Data.Ord`, `hask.Data.Functor`, `hask.Control.Applicative`, `hask.Control.Monad` | `Either` (`Left`, `Right`), `in_either`, `either`, `lefts`, `rights`, `isLeft`, `isRight`, `partitionEithers` |\n| `hask.Data.List` | `hask.lang`, `hask.Data.Foldable`, `hask.Data.Eq`, `hask.Data.Ord`, `hask.Data.Num`, `hask.Data.Maybe` | `head`, `last`, `tail`, `init`, `uncons`, `null`, `length`, `map`, `reverse`, `intersperse`, `intercalate`, `transpose`, `subsequences`, `permutations`, `foldl`, `foldl_`, `foldl1`, `foldl1_`, `foldr`, `foldr1`, `concat`, `concatMap`, `and_`, `or_`, `any`, `all`, `sum`, `product`, `minimum`, `maximum`, `scanl`, `scanl1`, `scanr`, `scanr1`, `mapAccumL`, `mapAccumR`, `iterate`, `repeat`, `replicate`, `cycle`, `unfoldr`, `take`, `drop`, `splitAt`, `takeWhile`, `dropWhile`, `dropWhileEnd`, `span`, `break_`, `stripPrefix`, `group`, `inits`, `tails`, `isPrefixOf`, `isSuffixOf`, `isInfixOf`, `isSubsequenceOf`, `elem`, `notElem`, `lookup`, `find`, `filter`, `partition`, `elemIndex`, `elemIndices`, `findIndex`, `findIndicies`, `zip`, `zip3`, `zip4`, `zip5`, `zip6`, `zip7`, `zipWith`, `zipWith3`, `zipWith4`, `zipWith5`, `zipWith6`, `zipWith7`, `unzip`, `unzip3`, `unzip4`, `unzip5`, `unzip6`, `unzip7`, `lines`, `words`, `unlines`, `unwords`, `nub`, `delete`, `diff`, `union`, `intersect`, `sort`, `sortOn`, `insert`, `nubBy`, `deleteBy`, `deleteFirstBy`, `unionBy`, `intersectBy`, `groupBy`, `sortBy`, `insertBy`, `maximumBy`, `minimumBy`, `genericLength`, `genericTake`, `genericDrop`, `genericSplitAt`, `genericIndex`, `genericReplicate` |\n| `hask.Data.String` | `hask.lang` | `words`, `unwords`, `lines`, `unlines` |\n| `hask.Data.Tuple` | `hask.lang` | `fst`, `snd`, `swap`, `curry`, `uncurry` |\n| `hask.Data.Char` | `hask.lang` | `isControl`, `isSpace`, `isLower`, `isUpper`, `isAlpha`, `isAlphaNum`, `isPrint`, `isDigit`, `isOctDigit`, `isHexDigit`, `isLetter`, `isMark`, `isNumber`, `isPunctuation`, `isSymbol`, `isSeparator`, `isAscii`, `isLatin1`, `isAsciiUpper`, `toLower`, `toUpper`, `toTitle`, `digitToInt`, `intToDigit`, `chr`, `ord` |\n| `hask.Data.Eq` | `hask.lang` | `Eq` (`==`, `!=`)\n| `hask.Data.Ord` | `hask.lang`, `hask.Data.Eq` | `Ord` (`\u003e`, `\u003c`, `\u003e=`, `\u003c=`), `Ordering` (`LT`, `EQ`, `GT`), `max`, `min`, `compare`, `comparing` |\n| `hask.Data.Functor` | `hask.lang` | `Functor` (`fmap`, `*`),  |\n| `hask.Data.Foldable` | `hask.lang` | `Foldable` (`foldr`, `foldr_`, `foldl`, `foldl_`, `foldr1`, `foldl1`, `toList`, `null`, `length`, `elem`, `maximum`, `minimum`, `sum`, `product`), `foldlM`, `foldrM`, `traverse_`, `for_`, `sequenceA_`, `mapM_`, `forM_`, `sequence_`, `concat`, `concatMap` , `and_`, `or_`, `all_`, `maximumBy_`, `minimumBy`, `notElem`, `find` |\n| `hask.Data.Traversable` | `hask.lang`, `hask.Data.Foldable`, `hask.Data.Functor` | `Traversable` (`traverse`, `sequenceA`, `mapM`, `sequence`), `for1`, `forM`, `mapAccumL`, `mapAccumR` |\n| `hask.Data.Monoid` | `hask.lang` | `Monoid` (`mappend`, `mempty`, `mconcat`) |\n| `hask.Data.Ratio` | `hask.lang`, `hask.Data.Num` | `Integral`, `Ratio` (`R`), `Rational`, `toRatio`, `toRational`, `numerator`, `denominator` |\n| `hask.Data.Num` | `hask.lang`, `hask.Data.Eq`, `hask.Data.Ord` | `Num` (`+`, `*`, `abs`, `signum`, `fromInteger`, `negate`, `-`), `Fractional` (`fromRational`, `/`, `recip`), `Floating` (`exp`, `sqrt`, `log`, `pow`, `logBase`, `sin`, `tan`, `cos`, `asin`, `atan`, `acos`, `sinh`, `tanh`, `cosh`, `asinh`, `atanh`, `acosh`), `Real` (`toRational`), `Integral` (`quotRem`, `quot`, `rem`, `div`, `mod`), `toRatio`, `RealFrac` (`properFraction`, `truncate`, `round`, `ceiling`, `floor`), `RealFloat` (`isNan`, `isInfinite`, `isNegativeZero`, `atan2`) |\n| `hask.Control.Applicative` | `hask.lang`, `hask.Data.Functor` | `Applicative` |\n| `hask.Control.Monad` | `hask.lang`, `hask.Control.Applicative`, `hask.Data.Functor` | `Monad` (`bind`, `\u003e\u003e`), `join`, `liftM` |\n| `hask.Python.builtins` | `hask.lang` | `callable`, `cmp`, `delattr`, `divmod`, `frozenset`, `getattr`, `hasattr`, `hash`, `hex`, `isinstance`, `issubclass`, `len`, `oct`, `repr`, `setattr`, `sorted`, `unichr` |\n| `hask.lang` | | `Show` (`show`), `Read`, `Eq`, `Ord`, `Enum` (`fromEnum`, `succ`, `pred`, `enumFrom`, `enumFromTo`, `enumFromThen`, `enumFromThenTo`), `Bounded`, `typeof`, `is_builtin`, `has_instance`, `nt_to_tuple`, `build_instance`, `Typeclass`, `Hask`, `TypedFunc`, `TypeSignatureError`, `undefined`, `caseof`, `m`, `p`, `IncompletePattnerError`, `data`, `d`, `deriving`, `H`, `sig`, `t`, `func`, `typify`, `NoGuardMatchException`, `guard`, `c`, `otherwise`, `instance`, `__`, `_t`, `_q`, `_i`, `List`, `L` |\n","funding_links":[],"categories":["Python","Awesome Functional Python"],"sub_categories":["Libraries"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbillpmurphy%2Fhask","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbillpmurphy%2Fhask","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbillpmurphy%2Fhask/lists"}