{ "info": { "author": "Matthew Egan Odendahl", "author_email": "hissp.gilch@xoxy.net", "bugtrack_url": null, "classifiers": [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Operating System :: Unix", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Topic :: Software Development", "Topic :: Software Development :: Code Generators", "Topic :: Software Development :: Compilers", "Topic :: Software Development :: Libraries" ], "description": "\n\n# Hissp\n\nIt's Python with a *Lissp*.\n\nHissp is a Lisp that compiles to a functional subset of Python.\nIt's the Python you know and love, with a powerful, streamlined skin.\n\n\n\n**Table of Contents**\n\n- [Hissp](#hissp)\n - [Philosophy and Goals](#philosophy-and-goals)\n - [Minimal implementation](#minimal-implementation)\n - [Interoperability](#interoperability)\n - [Useful error messages](#useful-error-messages)\n - [Syntax compatible with Emacs' `lisp-mode` and Parlinter](#syntax-compatible-with-emacs-lisp-mode-and-parlinter)\n - [Standalone output](#standalone-output)\n - [REPL](#repl)\n - [Same-module macro helpers](#same-module-macro-helpers)\n - [Modularity](#modularity)\n - [Show me some Code!](#show-me-some-code)\n - [The obligatory Hello, World!](#the-obligatory-hello-world)\n - [Calls. Hissp is literally all calls.](#calls-hissp-is-literally-all-calls)\n - [Literals and the Reader](#literals-and-the-reader)\n - [Calls and the compiler](#calls-and-the-compiler)\n - [FAQ (Frequently Anticipated Questions (and complaints))](#faq-frequently-anticipated-questions-and-complaints)\n - [Contributing](#contributing)\n - [Patches](#patches)\n - [Conduct](#conduct)\n - [good faith](#good-faith)\n - [professional detachment](#professional-detachment)\n - [disputes](#disputes)\n\n\n\n## Philosophy and Goals\n\n#### Radical Extensibility\nPython is already a really nice language, so why do we need Hissp?\n\nThe answer is *metaprogramming*: code that writes code.\nWhen you can shape the language itself to fit your problem domain,\nthe incomprehensible becomes obvious.\n\nPython really is a great language to work with.\n\"Executable pseudocode\" is not far off.\nBut it is too complex to be good at metaprogramming.\nThe use of `exec()` is frowned upon.\nIt's easy enough to understand, but hard to get right.\nPython Abstract Syntax Tree (AST)\nmanipulation is a somewhat more reliable technique,\nbut not for the faint of heart.\nPython AST is not simple, because Python isn't.\n\nHissp is a streamlined skin on Python:\na simplified AST that you can program in directly.\nHissp code is made of specially formatted tuples—easier\nto manipulate than Python AST,\nbut still more reliable than text manipulation.\nIn Hissp, code is just another kind of data.\n\nLisp is a programmable programming language,\nextensible though its renowned macro system\nwhich hooks into the compiler itself.\nMacros are Lisp's secret weapon.\nAnd Hissp brings this power to Python.\n\nAdding features that historically required a new version of the Python language,\nlike `with` statements, would be almost as easy as writing a new function in Lisp.\n\n#### Minimal implementation\nThe Hissp compiler includes what it needs to achieve its goals,\nbut no more. Bloat is not allowed.\nA goal of Hissp is to be as small as reasonably possible, but no smaller.\nWe're not code golfing here; readability still counts.\nBut this project has *limited scope*.\n\nHissp compiles to an upythonic *functional subset* of Python.\nThis subset has a direct and easy-to-understand correspondence to the Hissp code,\nwhich makes it straightforward to debug, once you understand Hissp.\nBut it is definitely not meant to be idiomatic Python.\nThat would require a much more complex compiler,\nbecause idiomatic Python is not simple.\n\n#### Interoperability\nWhy base a Lisp on Python when there are already lots of other Lisps?\n\nPython has a rich selection of libraries for a variety of domains\nand Hissp can use most of them as easily as the standard library.\nThis gives Hissp a massive advantage over other Lisps with less selection.\nIf you don't care to work with the Python ecosystem,\nperhaps Hissp is not the Lisp for you.\n\nNote that the Hissp compiler is written in Python 3.7.\n(Supporting older versions is not a goal,\nbecause that would complicate the compiler.\nThis may limit the available libraries.)\nBut because the compiler's target functional subset is so small,\nthe compiled output should still run fine on Python 3.5,\nprovided you aren't using any newer library features.\nRunning on even older versions (even Python 2)\nmay be possible if you avoid using certain newer Python language features.\n(Keyword-only arguments, for example.)\n\nPython code can also import and use packages written in Hissp,\nbecause they compile to Python.\n\n#### Useful error messages\nOne of Python's best features.\nAny errors that prevent compilation should be easy to find.\n\n#### Syntax compatible with Emacs' `lisp-mode` and Parlinter\nA language is not very usable without tools.\nHissp's basic reader syntax (Lissp) should work with Emacs.\n\n#### Standalone output\nOne can, of course, write Hissp code that depends on any Python library.\nBut the compiler does not depend on emitting calls out to any special\nHissp helper functions to work.\nYou do not need Hissp installed to run the final compiled Python output,\nonly Python itself.\n\nHissp includes some very basic Lisp macros to get you started.\nTheir expansions have no external requirements either.\n\nLibraries built on Hissp need not have this limitation.\n\n#### REPL\nA Lisp tradition, and Hissp is no exception.\nEven though it's a compiled language,\nHissp has an interactive shell like Python does.\nThe REPL displays the compiled Python and evaluates it.\nPrinted values use the normal Python reprs.\n(Translating those to Hissp is not a goal. Hissp is still Python.)\n\n#### Same-module macro helpers\nNot all Lisps support this, but Clojure is a notable exception.\nFunctions are generally preferable to macros when functions can do the job.\nThey're more reusable and composable.\nTherefore it makes sense for macros to delegate to functions where possible.\nBut such a macro should work in the same module.\nThis requires incremental compilation and evaluation of forms, like the REPL.\n\n#### Modularity\nThe Hissp language is made of tuples (and values), not text.\nThe basic reader included with the project just implements convenient\nway to write them.\nIt's possible to write Hissp in \"readerless mode\"\nby writing these tuples in Python.\n\nBatteries are not included because Python already has them.\nHissp's standard library is Python's.\nThere are only two special forms: quote and lambda.\nHissp does include a few basic macros and reader macros,\njust enough to write native unit tests,\nbut you are not obligated to use them when writing Hissp.\n\nIt's possible for an external project to provide an alternative\nreader with different syntax, as long as the output is Hissp code (tuples).\n\nBecause Hissp produces standalone output, it's not locked into any one Lisp paradigm.\nIt could work with a Clojure-like, Scheme-like, or Common-Lisp-like, etc.,\nreader, function, and macro libraries.\n\nIt is a goal of the project to support a more Clojure-like reader and\na complete function/macro library.\nBut while this informs the design of the compiler,\nit will be an external project in another repository.\n\n## Show me some Code!\nOr, the Hissp tutorial.\n\n### The obligatory Hello, World!\n```lisp\n(print \"Hello, World!\")\n```\nHonestly, that is 80% of Lisp right there. I told you it was simple.\nAnd the Python translation, as if you couldn't guess.\n```python\nprint(\"Hello, World!\")\n```\nYeah, we moved a parenthesis.\nLisp is so arcane, isn't it?\n\nHere's a more involved demonstration to whet you appetite.\nFor someone experienced in both Python and Lisp,\nthis may be enough to get you started,\nbut don't worry if you don't understand it all yet.\nThis will all be explained in more detail later on.\n```\n$ python -m hissp\n```\n```python\n#> (builtins..print 1 2j 3.0 [4,'5',6] : sep \":\")\n#..\n>>> __import__('builtins').print(\n... (1),\n... (2j),\n... (3.0),\n... [4, '5', 6],\n... sep=':')\n1:2j:3.0:[4, '5', 6]\n\n#> (hissp.basic.._macro_.define tuple* (lambda (: :* xs) xs))\n#..\n>>> # hissp.basic.._macro_.define\n... __import__('operator').setitem(\n... __import__('builtins').globals(),\n... 'tuplexSTAR_',\n... (lambda *xs:xs))\n\n```\n\n### Calls. Hissp is literally all calls.\nHissp has only two types of expressions or forms: literals, and calls.\n\n#### Literals and the Reader\nLiterals are handled at the reader level.\n\n\"The reader\" refer's to Hissp's basic parser.\nIt's the reader's job to translate the `.lissp` code files into Hissp code.\n\nIt's important to distinguish these two things,\nbecause they each have their own type of macro:\n\n* *Lissp* code is made of text, and the basic reader parses it into Hissp.\nYou can hook into this process with a *reader macro*,\nwhich can embed arbitrary Python objects into the Hissp.\n\n* *Hissp* code is made of tuples (and values), not text.\nLissp is to Hissp as the written word is to the spoken word.\nIt's ephemeral; it only lives in memory.\nThe compiler compiles Hissp to a *functional subset of Python*.\nYou can hook into this process with a *compiler macro*.\nWithout context suggesting otherwise,\nthe term *macro* refers to a *compiler macro*.\n\nLissp is a fairly direct *representation* of Hissp, but it's not the only one.\nOne could skip the reader altogether and write the Hissp in Python directly as tuples.\nThis is called \"readerless mode\".\nReader macros are an artifact of the reader and don't exist in readerless mode at all,\nbut compiler macros do work.\nIt's also possible to use alternative readers with alternate syntax,\nbut it must *represent* the same underlying tuples to be Hissp.\n\nUsing the basic reader,\nany valid Python literal (as defined by `ast..literal_eval`)\nis a valid Hissp literal,\nprovided it does not contain `\"`, `(`, `)`, or spaces\n(because then it would be read as multiple items) \nor start with a `'`.\n\nIn addition to the Python literals,\nthe basic reader has symbol literals, string literals,\nand is extensible with more literal types via reader macros.\n\nString literals begin and end with `\"` and may contain literal newlines,\nbut otherwise behave the same as Python's do.\n\nAnything else is a symbol.\nSymbols are allowed to contain many special characters, but because\nsymbols are meant to be used as Python identifiers,\nthe reader automatically munges invalid identifier characters to x-quoted words,\nlike `/` to `xSLASH_`.\nThis format was chosen because it contains an underscore\nand both lower-case and upper-case letters,\nwhich makes it distinct from standard Python naming conventions:\n`lower_case_with_underscores`, `UPPER_CASE_WITH_UNDERSCORES`. and `CapWords`.\nThis makes it easy to tell if an identifier contains munged characters.\nIt also cannot introduce a leading underscore,\nwhich can have special meaning in Python.\n\nA symbol that begins with a `:` is a \"keyword\".\nKeywords are never interpreted as identifiers,\nso they don't need to be quoted or munged.\n\nThe basic reader's macro syntax is limited to tagged forms,\nlike EDN and Clojure, but unlike Common Lisp\n(which could dispatch on any character),\nbecause it's meant to be compatible with existing tooling for syntax\nhighlighting and structural editing,\nwhich wouldn't work if you change the grammar.\n(An alternate reader for Hissp need not have this limitation.)\n\nReader macros in Lissp consist of a symbol ending with a `#`\nfollowed by another form.\nThe function named by the symbol is invoked on the form,\nand the reader embeds the resulting object into the output Hissp.\n\nFor example,\n```python\n#> builtins..float#inf\n>>> __import__('pickle').loads( # inf\n... b'\\x80\\x03G\\x7f\\xf0\\x00\\x00\\x00\\x00\\x00\\x00.'\n... )\ninf\n\n```\nThis inserts an actual `inf` object at read time into the Hissp code.\nSince this isn't a valid literal, it has to compile to a pickle.\nYou should normally try to avoid emitting pickles\n(e.g. use `(float 'inf)` or `math..inf` instead),\nbut note that a macro would get the original object,\nsince the code hasn't been compiled yet, which may be useful.\nWhile unpickling does have some overhead,\nit may be worth it if constructing the object normally has even more.\nNaturally, the object must be picklable to emit a pickle.\n\nUnqualified reader macros are reserved for the basic Hissp reader.\nThere are currently three of them: `.#`, `_#`, and `$#`.\n\nIf you need more than one argument for a reader macro, use the built in\n`.#` macro, which evaluates a form at read time. For example,\n```python\n#> .#(fractions..Fraction 1 2)\n#..\n>>> __import__('pickle').loads( # Fraction(1, 2)\n... b'\\x80\\x03cfractions\\nFraction\\nX\\x03\\x00\\x00\\x001/2\\x85R.'\n... )\nFraction(1, 2)\n\n```\n\nThe `_#` macro omits the next form.\nIt's a way to comment out code,\neven if it takes multiple lines.\n\nThere are also four more built-in reader macros that don't end with `#`:\n* ``` ` ``` template quote\n* `,` unquote\n* `,@` splice unquote\n* `'` quote\n\nThe final builtin `$#` creates a gensym based on the given symbol.\nWithin a template, the same gensym literal always makes the same\ngensym.\n```python\n#> `($#hiss $#hiss)\n#..\n>>> (lambda *xAUTO0_:xAUTO0_)(\n... '_hissxAUTO..._',\n... '_hissxAUTO..._')\n('_hissxAUTO..._', '_hissxAUTO..._')\n\n```\n\nIn readerless mode, these reader macros correspond to functions used to\nmake the Hissp itself.\nFor example, one could make a quoting \"readerless macro\" like this\n\n```python\n>>> def q(form):\n... return 'quote', form\n>>> from hissp.compiler import readerless\n>>> readerless(\n... ('print', q('hi'),),\n... )\n\"print(\\n 'hi')\"\n>>> print(_)\nprint(\n 'hi')\n>>> eval(_)\nhi\n\n```\nWhich is equivalent to\n```python\n#> (print 'hi)\n#..\n>>> print(\n... 'hi')\nhi\n\n```\n\n#### Calls and the compiler\n\nHere's a little more Hissp-specific example.\nNote the lack of commas between arguments.\n```\n$ python -m hissp\n```\n```python\n#> (builtins..print 1 2j 3.0 [4,'5',6] : sep \":\")\n#..\n>>> __import__('builtins').print(\n... (1),\n... (2j),\n... (3.0),\n... [4, '5', 6],\n... sep=':')\n1:2j:3.0:[4, '5', 6]\n\n```\nThis is the basic Hissp REPL.\nIt shows the Python compilation and its result.\n\nThat `[4,'5',6]` is read as a single literal. Note the lack of spaces.\nThe double-quoted string literal is an exception. It may have spaces.\nAnd unlike Python, it is allowed to contain literal newlines.\n\nThe `builtins..print` is an example of a *qualified symbol*,\nwhich is a kind of implicit import.\nThese are of the form `..`, and are important to make\nmacros work properly across modules.\nYou don't have to qualify builtins normally, but in a macroexpansion,\nthis allows the macro to work even if the builtin name has been\nshadowed by a local variable in that context.\nNote that package names may contain dots, as in Python.\n\nThe `:` separates the single arguments from the paired arguments,\nwhich either pair a value with a unique key like `sep \":\"` or with the\nspecial unpacking keywords `:*` and `:**`, like `:* args` or `:** kwargs`,\nwhich, like Python, can be repeated.\n\nHissp's two special forms deserve special consideration.\nThese are calls that are built into the compiler.\nUnlike a normal function call, special forms are evaluated at compile time.\n\nThe first special form is `quote`. It returns its argument unevaluated.\n```python\n#> (quote builtins..print)\n#..\n>>> 'builtins..print'\n'builtins..print'\n\n```\nThe distinction between symbols and strings only applies to the reader.\nHissp has no separate symbol type.\nA quoted symbol just emits a string.\n\nHere's the earlier example quoted.\n```python\n#> (quote (builtins..print 1 2j 3.0 [4,'5',6] : sep \":\"))\n#..\n>>> ('builtins..print', 1, 2j, 3.0, [4, '5', 6], ':', 'sep', ('quote', ':', {':str': True}))\n('builtins..print', 1, 2j, 3.0, [4, '5', 6], ':', 'sep', ('quote', ':', {':str': True}))\n\n```\nThis reveals how to write the example in readerless mode.\nNotice the reader adds some metadata ``{':str': True}``\nto quoted strings that were read from double-quoted strings.\nArguments to ``quote`` after the first have no effect on compilation,\nbut may be useful to macros and reader macros.\nMany literal types simply evaluate to themselves and so are unaffected by quoting.\nThe exceptions are strings and tuples, which can represent identifiers and calls.\n\nQuoting is important enough to have a special reader macro.\n`'foo` is the same as `(quote foo)`.\n\nThe second special form is `lambda`\nThe first argument of a lambda is the pararmeters tuple.\nLike calls, the `:` separates the single from the paired (if any).\nAfter the parameters tuple, the rest of the arguments are the function body.\n\n```python\n#> (lambda (a b ; single/positional\n#.. : e 1 f 2 ; paired/kwargs\n#.. :* args h 4 i :? j 1 ; *args and kwonly\n#.. :** kwargs)\n#.. 42)\n#..\n>>> (lambda a,b,e=(1),f=(2),*args,h=(4),i,j=(1),**kwargs:(42))\n at ...>\n\n#> (lambda (: :* :? x :?)) ; Only kwonly. Empty body returns ().\n#..\n>>> (lambda *,x:())\n at ...>\n\n#> (lambda (a b c)\n#.. (print a)\n#.. (print b)\n#.. c)\n#..\n>>> (lambda a,b,c:(\n... print(\n... a),\n... print(\n... b),\n... c)[-1])\n at ...>\n\n```\n\nNormal call forms evaluate their arguments before calling the function,\nas Python does.\nSpecial forms are different—`quote`'s argument is not evaluated at all.\nThe body of a lambda is not evaluated until the function is invoked,\nand its parameter tuple is partly evaluated (if there are defaults) and\npartly quoted.\nWhile there are only two special forms, the compiler is extensible via macros.\nLike special forms, macro calls do not have to evaluate their arguments.\n\nMacros are simply functions that take Hissp code, and return Hissp code.\nWhen an unqualified symbol is in the function position of a tuple about\nto be evaluated, the compiler checks if the module's `_macro_` namespace\nhas that symbol. If it does, it is called at compile time as a macro and\nthe result is inserted into the code in its place.\n\nQualified symbols can also be macros if looked up directly from their module's `_macro_`.\nE.g. `(hissp.basic.._macro_.define FOO 0xf00)`\n\nThe `hissp.basic.._macro_.defmacro` macro defines a function in the module's macro space,\ncreating `_macro_` if it doesn't exist yet.\nBut the compiler doesn't care how it gets there:\n`_macro_` functions are macros regardless. This means \"importing\" a macro is as simple\nas adding it to the current module's macro space.\n\n## FAQ (Frequently Anticipated Questions (and complaints))\n\n> Anticipated? Didn't you mean \"asked\"?\n\nWell, this project is still pretty new.\n\n> Can Hissp really do anything Python can when it only compiles to a subset of it?\n\nYes.\n\nShort proof: Hissp has strings and can call `exec()`.\n\nBut you usually won't need it because you can import anything written in Python\nby using qualified symbols.\n\nHissp macros and reader macros can return any type of object.\nIf you return a string the compiler will assume it's either a qualified symbol\nor plain identifier (and emit it verbatim).\nBut, like a SQL injection attack,\nthe string *could* contain almost arbitrary Python code instead.\n\nHowerver, the whole point of Hissp is syntactic macros.\nIf you wanted to do string metaprogramming you could have just used `exec()`,\nso you're giving up a lot of Hissp's power.\nExpressions are relatively safe if you're careful,\nbut note that statements would only work at the top level.\n\nIn principle, you never *need* to do this.\nIt's dirty. It's risky.\nIt's worse than `eval()`/`exec()`, which are at least explicit about it.\nEven if you think you need it, you still probably don't.\nBut it can be very useful as an optimization.\n\n> What's 1 + 1?\n\nTwo.\n\n> I mean how do you write it in Hissp without operators? Please don't say `eval()`.\n\nWe have all the operators because we have all the standard library functions.\n```lisp\n(operator..add 1 1)\n```\n\n> That's really verbose though.\n\nYou can, of course, abbreviate these.\n```python\n#> (define + operator..add)\n#..\n>>> # define\n... __import__('operator').setitem(\n... __import__('builtins').globals(),\n... 'xPLUS_',\n... __import__('operator').add)\n\n#> (+ 1 1)\n#..\n>>> xPLUS_(\n... (1),\n... (1))\n2\n\n```\nYes, `+` is a valid symbol. It gets munged to `xPLUS_`.\nThe result is all of the operators you might want,\nusing the same prefix notation used by all the calls.\n\n> I want infix notation!\n\nHissp is a Lisp. It's all calls! Get used to it.\n\nFully parenthesized prefix notation is explicit and consistent.\nIt's very readable if properly indented.\nDon't confuse \"easy\" with \"familiar\".\nAlso, you don't have to be restricted to one or two arguments.\n\n> ...\n\nFine. You can write macros for any syntax you please.\n\nAlso recall that (reader) macros can return arbitrary Python snippets\nand the compiler will emit them verbatim.\nYou should generally avoid doing this,\nbecause then you're metaprogramming with strings instead of AST.\nYou're giving up a lot of Hissp's power.\nBut optimizing complex formulas is maybe one of the few times it's OK to do that.\n\nRecall the `.#` reader macro executes a form and embeds its result into the Hissp.\n\n```python\n#> (define quadratic\n#.. (lambda (a b c)\n#.. .#\"(-b + (b**2 - 4*a*c)**0.5)/(2*a)\"))\n#..\n>>> # define\n... __import__('operator').setitem(\n... __import__('builtins').globals(),\n... 'quadratic',\n... (lambda a,b,c:(-b + (b**2 - 4*a*c)**0.5)/(2*a)))\n\n```\n\nBut for a top-level `define` like this, you could have just used `exec()`.\n\n> How do I start the REPL again?\n\nIf you installed the distribution using pip,\nyou can use the provided `hissp` console script.\n```\n$ hissp\n```\nYou can also launch the Hissp package directly\nusing an appropriate Python interpreter from the command line\n```\n$ python3 -m hissp\n```\n\n> There are no statements?! How can you get anything done?\n\nThere are expression statements only (each top-level form). That's plenty.\n\n> But there's no assignment statement!\n\nThat's not a question.\n\nFor any complaint of the form \"Hissp doesn't have feature X\",\nthe answer is usually \"Write a macro to implement X.\"\n\nUse the `hissp.basic.._macro_.define` and `hissp.basic.._macro_.let` macros for globals\nand locals, respectively.\nLook at their expansions and you'll see they don't use assignment statements either.\n\nSee also `builtins..setattr` and `operator..setitem`.\n\n> But there's no `macroexpand`. How do I look at expansions?\n\nInvoke the macro indirectly so the compiler sees it as a normal function.\n`((getattr hissp.basic.._macro_ \"define\") 'foo '\"bar\")`\nOne could, of course, write a function or macro to automate this.\n\nBut you can just look at the compiled output.\nIt's indented, so it's not that hard to read.\nThe compiler also helpfully includes a comment in the compiled output\nwhenever it expands a macro.\n\n> There's no `for`? What about loops?\n\nTry recursion. `list()`, `map()` and `filter()` plus lambda can do\nanything list comprehensions can. Ditch the `list()` for lazy generators.\nReplace `list()` with `set()` for set comps. Dict comps are a little trickier.\nUse `dict()` on an iterable of pairs. `zip()` is an easy way to make them,\nor just have the map's lambda return pairs.\n\n> This is so much harder than comprehensions!\n\nNot really. But you can always write a macro if you want different syntax.\nYou can pretty easily implement comprehensions this way.\n\n> That's comprehensions, but what about `for` statements?\nYou don't really think I should build a list just to throw it away?\n\nSide effects are not good functional style.\nAvoid them for as long as possible.\nStill, you do need them eventually if you want your program to do anything.\n\nUse `any()` for side-effects to avoid building a list.\nUsually, you'd combine with `map()`, just like the comprehensions.\nMake sure the lambda returns `None`s (or something false),\nbecause a true value acts like `break` in `any()`.\nObviously, you can use this to your advantage if you *want* a break,\nwhich seems to happen pretty often when writing imperative loops.\n\nSee also `itertools`, `builtins..iter`.\n\n> There's no `if` statement. Branching is fundamental!\n\nNo it's not. You already learned how to `for` loop above.\nIsn't looping zero or one times like skipping a branch or not?\nNote that `False` and `True` are special cases of `0` and `1` in Python.\n`range(False)` would loop zero times, but `range(True)` loops one time.\n\n> What about if/else ternary expressions?\n\n```python\n(lambda b, *then_else: then_else[not b]())(\n1 < 2,\nlambda: print('yes'),\nlambda: print('no')\n)\n```\nThere's a `hissp.basic.._macro_.if-else` macro that basically expands to this.\nI know it's a special form in other Lisps (or `cond` is),\nbut Hissp doesn't need it. Smalltalk pretty much does it this way.\nOnce you have `if` you can make a `cond`. Lisps actually differ on which\nis the special form and which is the macro.\n\n> You have to define three lambdas just for an `if`?!\n isn't this really slow? It really ought to be a special form.\n\nIt's not *that* slow.\nLike most things, it's really only an issue in a bottleneck.\nIf you find one, there's no runtime overhead for using `.#` to inject some Python.\n\nAlso recall that macros are allowed to return strings of Python code.\nAll the usual caveats for text-substitution macros apply.\nUse parentheses.\n```lisp\n(defmacro !if (test then otherwise)\n \"Compiles to if/else expression.\"\n (.format \"(({}) if ({}) else ({}))\"\n : :* (map hissp.compiler..readerless\n `(,then ,test ,otherwise))))\n```\nEarly optimization is the root of all evil.\nDon't use text macros unless you really need them.\nEven if you think you need one, you probably don't.\n\nSyntactic macros are powerful not just because they can delay evaluation,\nbut because they can read and re-write code.\nUsing a text macro like the above can hide information that a syntactic\nrewriting macro needs to work properly.\n\n> Does Hissp have tail-call optimization?\n\nNo, because CPython doesn't.\nIf a Python implementation has it, Hissp will too,\nwhen run on that implementation.\nBut you can increase the recursion limit with `sys..setrecursionlimit`.\nBetter not increase it too much if you don't like segfaults, but\nyou can trampoline instead. See Drython's `loop()` function. Or use it.\nClojure does it about the same way.\n\n> How do I make a tuple?\n\nUse `tuple()`.\n\n> But I have to already have an iterable, which is why I wanted a tuple in the first place!\n\n`lambda *a:a`\n\nYou can also make an empty list with `[]` or `(list)`, and then `.append` to it.\n(Try the `cascade` macro.)\nFinally, the template syntax ``` `()``` makes tuples. Unquote `,` calls/symbols if needed.\n\n> How do I make a class?\n\nUse `type()`. (Or whatever metaclass.)\n\n> Very funny. That just tells me what type something is.\n\nNo, seriously, you have to give it all three arguments. Look it up.\n\n> Well now I need a dict!\n\nUse `dict()`. Obviously.\nYou don't even need to make pairs if the keys are identifiers.\nJust use kwargs.\n\n> That seems too verbose. In Python it's easier.\n\nYou mostly don't need classes though.\nClasses conflate data structures with the functions that act on them,\nand tend to encourage fragmented mutable state which doesn't scale well.\nThey're most useful for their magic methods to overload operators and such.\nBut Hissp mostly doesn't need that since it has no operators to speak of.\n\nAs always, you can write a function or macro to reduce boilerplate.\nThere's actually a `hissp.basic.._macro_.deftype` macro for making a top-level type.\n\n> How do I raise exceptions?\n\n`(operator..truediv 1 0)` seems to work.\nExceptions tend to raise themselves if you're not careful.\n\n> But I need a raise statement for a specific exception message.\n\nExceptions are not good functional style.\nYou probably don't need them.\nIf you must, you can still use `exec()`.\n(Or use Drython's `Raise()`.) \n\n> Use exec? Isn't that slow? \n\nIf the exceptions are only for exceptional cases, then does it matter?\nEarly optimization is the root of all evil.\n\n> What about catching them?\n\nTry not raising them in the first place?\nOr `contextlib..suppress`.\n \n> But there's no `with` statement either!\n\nUse `contextlib..ContextDecorator`\nas a mixin and any context manager works as a decorator.\nOr use Drython's `With()`.\n\n> How do I use a decorator?\n\nYou apply it to the function (or class):\ncall it with the function as its argument.\nDecorators are just higher-order functions.\n\n> Any context manager? But you don't get the return value of `__enter__()`!\nAnd what if it's not re-entrant?\n\n`suppress` work with these restrictions, but point taken.\nYou can certainly call `.__enter__()` yourself, but you have to call\n`.__exit__()` too. Even if there was an exception.\n\n> But I need to handle the exception if and only if it was raised,\n for multiple exception types, or I need to get the exception object.\n\nUse `exec()` with callbacks in its locals.\n\n> Isn't this slow?! You can't get away with calling this an \"exceptional case\" this time.\nThe happy path would still require compiling an exec() string!\n\nNot if you define it as a function in advance.\nThen it only happens once on module import.\nSomething like,\n```lisp\n(exec \"\ndef try_statement(block, target, handler):\n try:\n block()\n except target as ex:\n handler(ex)\")\n```\nOnce on import is honestly not bad. Even the standard library does it,\nlike for named tuples.\nBut at this point,\nunless you really want a single-file script with no dependencies,\nyou're better off defining the helper function in Python and importing it.\nYou could handle the finally/else blocks similarly.\nSee Drython's `Try()` for how to do it. Or just use Drython.\n\n> Isn't Hissp slower than Python? Isn't Python slow enough already?\n\n\"Slow\" only matters if it's in a bottleneck.\nHissp will often be slower than Python,\nbecause it compiles to a functional subset of Python that relies on\ndefining and calling functions more.\nBecause Python is a multiparadigm language,\nit is not fully optimized for the functional style,\nthough some implementations may do better than CPython here.\n\nEarly optimization is the root of all evil.\nAs always don't fix it until it matters,\nthen profile to find the bottleneck and fix only that part.\nYou can always re-write that part in Python (or C).\n\n> Yield?\n\nWe've got itertools.\nCompose iterators functional-style.\nYou don't need yield.\n\n> But I need it for co-routines. Or async/await stuff. How do I accept a send?\n\nMake a `collections.abc..Geneartor` subclass with a `send()` method.\n\nOr use Drython's `Yield()`.\n\nGenerator-based coroutines have been deprecated.\nDon't implement them with generators anymore.\nNote there are `collections.abc..Awaitable` and `collections.abc..Coroutine`\nabstract base classes too.\n\n> How do I add a docstring to a module/class/function?\n\nAssign a string to the `__doc__` attribute of the module/class/function object.\nThat means defining a global in the module,\nor a key in the dict argument to `type()` also works.\n\n> The REPL is nice and all, but how do I compile a module?\n\n```lisp\n(hissp.reader..transpile \"hissp\" \"basic\")\n```\nor\n```python\nfrom hissp.reader import transpile\n\ntranspile(__package__, \"spam\", \"eggs\")\n```\nConsider putting the above in `__init__.py` to auto-compile\neach Hissp module in the package on package import during development.\nYou can disable it again on release, if desired,\nbut this gives you fine-grained control over what gets compiled when.\nNote that you usually would want to recompile the whole project\nrather than only the changed files like Python does,\nbecause macros run at compile time.\nChanging a macro in one file normally doesn't affect the code that uses\nit in other files until they are recompiled.\n\n> How do I import things?\n\nJust use a qualified symbol. You don't need imports.\n\n> But it's in a deeply nested package with a long name. It's tedious!\n\nSo assign it to a global.\nJust don't do this in the macroexpansions where it might end up in another module.\n\n> But I need the module object itself!\nThe package `__init__.py` doesn't import it or it's not in a package.\n\nUse `importlib..import_module`.\n```python\n#> (importlib..import_module 'collections.abc)\n#..\n>>> __import__('importlib').import_module(\n... 'collections.abc')\n\n\n```\n\n> How do I import a macro?\n\nThe same way you import anything else.\nPut it in the `_macro_`\nnamespace if you want it to be an active module-local macro.\nThe compiler doesn't care how it gets there, but\nthere's a nice `hissp.basic.._macro_.from-require`\nmacro if you want to use that.\n\n> How do I write a macro?\n\nMake a function that accepts the syntax you want as parameters and\nreturns its transformation as Hissp code\n(the template reader syntax makes this easy).\nPut it in the `_macro_` namespace.\nThere's a nice `hissp.basic.._macro_.defmacro` to do this for you.\nIt will even create the namespace if it doesn't exist yet.\n\nSome tips:\n* Hissp macros are very similar to Clojure or Common Lisp macros.\n * Tutorals on writing macros in these languages are mostly applicable to Hissp.\n* Output qualified symbols so it works in other modules.\n * The template reader syntax does this for you automatically.\n * You have to do this yourself in readerless mode.\n * You can interpolate an unqualified symbol into a template by unquoting it,\n same as any other value.\n* Use gensyms (`$#spam`) to avoid accidental capture of identifiers.\n\n> How do I write a reader macro?\n\nMake a function that accepts the syntax you want as its parameter and\nreturns its transformation as Hissp code.\n\n> Why the weird prompts at the REPL?\n\nThe REPL is designed so that you can copy/paste it into doctests\nor Jupyter notebook cells running an IPython kernel and it should just work.\nIPython will ignore the Lissp because its `#>`/`#..`\nprompts makes it look like a Python comment,\nand it's already set up to ignore the initial `>>> `/`...`.\nBut doctest expects these,\nbecause that's what the Python shell looks like.\n\n## Contributing\nThere are many ways to contribute to an open-source project,\neven without writing code.\n\nQuestions are allowed on the issue tracker,\nthey help illustrate deficiencies in our documentation,\nbut do check there first!\n\nBug reports are welcome.\n\nPRs to help improve documentation,\nstructure, or compatibility will also be considered.\n\n### Patches\nPRs must be aligned with the philosophy and goals of the project to be\nconsidered for inclusion.\n\nPRs do not have to be *perfect* to be submitted,\nbut must be perfect enough to pass peer review before they are merged in.\nSmall, focused changes are more likely to be reviewed.\n\nChanges to the source code must be properly formatted and have full test\ncoverage before the PR can be accepted.\nManual tests may suffice for configuration files.\nOur Python source uses Black formatting.\nDisable this using `# fmt: off` tags for \"readerless mode\" Hissp snippets\nwhich should be formatted Lisp-style (play with Parinfer until you get it),\nor anywhere the extra effort of manual formatting is worth it.\nIn readerless mode, Hissp tuples shall always include the trailing `,`.\nFollow PEP 8 even when Black has no opinion.\nOur .lissp source uses Emacs lisp-mode for indentation.\nIt must also pass Parlinter.\n\nDocumentation is expected to have correct (American English) spelling\nand grammar. All Doctests must pass.\n\nYou can use pytest to run unittests and doctests at the same time.\nMake sure you install the dev requirements first.\nHissp has no dependencies, but its test suite does.\n```\n$ pip install -r requirements-dev.txt\n```\n```\n$ pytest --doctest-modules --cov=hissp\n```\n\nWe merge to master without squashing.\nCommits must be small enough to be reviewable.\nWe don't accept PRs on faith.\n\nNote section 5 of the LICENSE.\nYou must have the legal rights to the patch to submit them under those terms:\neither you own the copyright\n(e.g. because you are the author of the patch and it was not a work for hire)\nor have appropriate license to do it.\n\nThe git repository itself will normally suffice as a record of\nauthorship for copyright purposes.\nDon't update the original boilerplate notices on each file.\nBut commits authored by or owned by someone else must be clearly labeled as such.\nNo plagiarism will be permitted,\neven if you're copying something from the public domain.\nWe may maintain a NOTICE file per section 4.(d) of the LICENSE if needed.\n\n### Conduct\nTo encourage participation,\nwe strive to maintain an environment conducive to cooperation and\ncollaborative effort. To that end, note that\nGitHub's acceptable use and conduct restrictions apply to this repo as well.\nSpamming, doxing, harassment, etc. will not be tolerated.\nBelligerent users may be blocked at the discretion of the project\nmaintainer, even on the first offense.\nIssues that become heated or veer too far off topic may be closed or locked.\n#### good faith\nSoftware Engineers are an eccentric bunch who come from various\ngenerational and world cultures.\nTone is notoriously hard to convey in writing and for many English is\nnot their first language.\nTherefore, before you reply in anger, please\n[assume good faith](https://en.wikipedia.org/wiki/Wikipedia:Assume_good_faith)\nuntil there is very obvious evidence to the contrary.\n#### professional detachment\nYou are free to criticize code, documentation, PRs, philosophy, ideas,\nand so on, if relevant to the project, but not each other.\nDiscussion here should be focused on the work, not the people doing it.\nThis is not the forum for status games.\nContributions alone demonstrate competence.\nIssue posts stereotyping, psychoanalyzing, or otherwise categorizing people \nbeyond legitimate project roles\n(or other such veiled insults, even if spun in a \"positive\" light)\nincluding similar attempts to aggrandize oneself\n(even if spun as self-deprecating)\nare, at the very least, off-topic, and subject to moderator action.\n#### disputes\nWhile consensus based on stated project goals is usually preferable,\nthe maintainer is the final arbiter for the project.\nHe may reinterpret goals, instate rules, and reject contributions.\nEven if you disagree with his decision,\nyou may always make a fork as permitted by the LICENSE.\nJust don't call it Hissp (see section 6 of the LICENSE).", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/gilch/hissp", "keywords": "lisp macro metaprogramming compiler DSL AST transpiler emacs clojure scheme", "license": "Apache-2.0", "maintainer": "", "maintainer_email": "", "name": "hissp", "package_url": "https://pypi.org/project/hissp/", "platform": "", "project_url": "https://pypi.org/project/hissp/", "project_urls": { "Homepage": "https://github.com/gilch/hissp" }, "release_url": "https://pypi.org/project/hissp/0.1.0/", "requires_dist": null, "requires_python": ">=3.7", "summary": "It's Python with a Lissp.", "version": "0.1.0" }, "last_serial": 5466393, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "8e474a886104db67ac5fcac6be6ddfd3", "sha256": "58b440da3588d2f3e792eda93acd5dbc540b0999f088219854ea1736ee6cf957" }, "downloads": -1, "filename": "hissp-0.1.0.tar.gz", "has_sig": false, "md5_digest": "8e474a886104db67ac5fcac6be6ddfd3", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 54885, "upload_time": "2019-06-29T21:28:26", "url": "https://files.pythonhosted.org/packages/db/0f/f6f467b8ffeb8562984cf761d8dcd23e6957604a628a18a9aedd157ed3ba/hissp-0.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8e474a886104db67ac5fcac6be6ddfd3", "sha256": "58b440da3588d2f3e792eda93acd5dbc540b0999f088219854ea1736ee6cf957" }, "downloads": -1, "filename": "hissp-0.1.0.tar.gz", "has_sig": false, "md5_digest": "8e474a886104db67ac5fcac6be6ddfd3", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 54885, "upload_time": "2019-06-29T21:28:26", "url": "https://files.pythonhosted.org/packages/db/0f/f6f467b8ffeb8562984cf761d8dcd23e6957604a628a18a9aedd157ed3ba/hissp-0.1.0.tar.gz" } ] }