diff --git a/ihaskell.cabal b/ihaskell.cabal index a033bdd3..751bd83b 100644 --- a/ihaskell.cabal +++ b/ihaskell.cabal @@ -164,6 +164,7 @@ Test-Suite hspec IHaskell.Test.Completion IHaskell.Test.Util IHaskell.Test.Parser + IHaskell.Test.Hoogle default-language: Haskell2010 build-depends: base, @@ -178,6 +179,7 @@ Test-Suite hspec directory, text, shelly, + raw-strings-qq, setenv source-repository head diff --git a/notebooks/IHaskell.ipynb b/notebooks/IHaskell.ipynb index 2818d75f..7dcae964 100644 --- a/notebooks/IHaskell.ipynb +++ b/notebooks/IHaskell.ipynb @@ -2,10 +2,7 @@ "cells": [ { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "![](https://camo.githubusercontent.com/f6540337202bb3b0c2545d90de0791c9196f9510/68747470733a2f2f7261772e6769746875622e636f6d2f67696269616e736b792f494861736b656c6c2f6d61737465722f68746d6c2f6c6f676f2d36347836342e706e67)\n", "\n", @@ -21,11 +18,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -54,10 +47,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "As you can see, each input cell get an execution number. The first input cell is labeled `In [1]`. Just like in GHCi, the output of the last executed statement or expression is available via the `it` variable - however, in addition, the output of the $n$th cell is available via the `itN` variable. For example, if we wanted to see what the first cell printed, we can go ahead and output that:" ] @@ -65,11 +55,7 @@ { "cell_type": "code", "execution_count": 2, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -87,10 +73,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "In addition to simple code cells such as the ones you see, you can also have other types of cells. All of this inline text, for instance, is written using Markdown cells, which support the majority of Github markdown syntax. This lets you embed images and formatting and arbitrary HTML interspersed with your Haskell code. In addition, you can export these notebooks into HTML or even as presentations using `reveal.js`. \n", "\n", @@ -100,11 +83,7 @@ { "cell_type": "code", "execution_count": 3, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -127,10 +106,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "In addition to multi-line expressions, IHaskell supports most things that you could put in a standard Haskell file. For example, we can have function bindings without the `let` that GHCi requires. (As long as you group type signatures and their corresponding declarations together, you can use pattern matching and put signatures on your top-level declarations!)" ] @@ -138,11 +114,7 @@ { "cell_type": "code", "execution_count": 4, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -174,10 +146,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "So far we've just looked at pure functions, but nothing is stopping us from doing IO." ] @@ -185,11 +154,7 @@ { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -207,10 +172,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "IHaskell supports most GHC extensions via the `:extension` directive (or any shorthand thereof)." ] @@ -218,11 +180,7 @@ { "cell_type": "code", "execution_count": 6, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -329,11 +287,7 @@ { "cell_type": "code", "execution_count": 7, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "-- And enable extensions.\n", @@ -343,10 +297,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "Data declarations do pretty much what you expect, and work fine on multiple lines. If a declaration turns out to be not quite what you wanted, you can just go back, edit it, and re-evaluate the code cell." ] @@ -354,11 +305,7 @@ { "cell_type": "code", "execution_count": 8, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -382,10 +329,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "Although this doesn't hold everywhere, we've tried to keep IHaskell relatively similar to GHCi in terms of naming. So, just like in GHCi, you can inspect types with `:type` (or shorthands):" ] @@ -393,11 +337,7 @@ { "cell_type": "code", "execution_count": 9, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -500,10 +440,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "The same goes for the `:info` command. However, unlike GHCi, which simply prints info, the IHaskell notebook brings up a separate pane." ] @@ -511,11 +448,7 @@ { "cell_type": "code", "execution_count": 10, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": {}, @@ -530,10 +463,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "If you're looking at this notebook after it's been exported to HTML, you won't be able to see this interactive pane that pops up after this is evaluated. However, you can disable the interactive pager, and instead just show the output below the cell:" ] @@ -541,11 +471,7 @@ { "cell_type": "code", "execution_count": 11, - "metadata": { - "collapsed": true, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "-- Only takes effect on later cells, so stick it in its own cell.\n", @@ -555,11 +481,7 @@ { "cell_type": "code", "execution_count": 12, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": {}, @@ -691,10 +613,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "We can now write slightly more complicated scripts." ] @@ -702,11 +621,7 @@ { "cell_type": "code", "execution_count": 13, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -734,10 +649,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "This is where the similarities with GHCi end, and the particularly shiny features of IHaskell begin.\n", "\n", @@ -747,11 +659,7 @@ { "cell_type": "code", "execution_count": 14, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "data Color = Red | Green | Blue" @@ -759,10 +667,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "If we were playing around with designing GUI applications, for instance, we might want to actually *see* these colors, instead of just seeing the text \"Red\", \"Green\", and \"Blue\" when we are debugging.\n", "\n", @@ -772,11 +677,7 @@ { "cell_type": "code", "execution_count": 15, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "import IHaskell.Display\n", @@ -794,10 +695,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "Once we define a custom `display :: a -> IO Display` function, we can simply output a `Color`:" ] @@ -805,11 +703,7 @@ { "cell_type": "code", "execution_count": 16, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -1090,10 +984,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "The `DisplayData` type has several constructors which let you display your data as plain text, HTML, images (SVG, PNG, JPG), or even as LaTeX code.\n", "\n", @@ -1104,10 +995,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "The `ihaskell-aeson` package adds a display for [Aeson](http://hackage.haskell.org/package/aeson) JSON `Value` types. These are automatically formatted as JSON, rather than as Haskell values:" ] @@ -1115,11 +1003,7 @@ { "cell_type": "code", "execution_count": 17, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -1169,10 +1053,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "The `ihaskell-blaze` package lets you play around with HTML straight from within IHaskell using the [Blaze](http://jaspervdj.be/blaze/tutorial.html) library." ] @@ -1180,11 +1061,7 @@ { "cell_type": "code", "execution_count": 18, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -1413,10 +1290,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "The `ihaskell-diagrams` package allows you to experiment with the [diagrams](http://projects.haskell.org/diagrams/) package. It requires the Cairo backend." ] @@ -1424,15 +1298,11 @@ { "cell_type": "code", "execution_count": 19, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAIAAAD2HxkiAAAABmJLR0QA/wD/AP+gvaeTAAAXMElEQVR4nO3dWXAU1R4G8O6ZyZAoIAWoyL5IFShaWqWlZRX6YOkbD76il9LCCTNZCGEJBjFsYQ0QgbCrIISghB0ETCgWAdmXQBIIIQsJkZCQQNZJz9J9H2LFVJZJp/t0nz493+/p3gt89ffgd7szPfMffsyYMePHj+cAgIZz587Zxo8fv2XLFtqTAASp8PBwC+0ZAIIdSghAGUoIQBlKCEAZSghAGUoIQBlKCEAZSghAGUoIQBlKCEAZSghAGUoIQBlKCEAZSghAGUoIQBlKCEAZSghAGUoIQBlKCEAZSghAGUoIQBlKCEAZSghAGUoIQBlKCEAZSghAGUoIQBlKCEAZSghAGUoIQBlKCEAZSghAGUoIQBlKCEAZSghAGUoIQBlKCEAZSghAGUoIQBlKCEAZSghAGUoIQBlKyDZJkiRJoj0FqIISsi01NXXXrl20pwBVbLQHAOUaGhqmT5/OcdwXX3zx4osv0h4HFMKVkGFLly6tr6+vq6tbunQp7VlAOZSQVaWlpUlJSU1NTYIgrFixori4mPZEoBBKyKro6OiW/8zzfGxsLMVhQA2UkEnnz58/cuSIx+Np/q8ej+fw4cNnzpyhOhQohBKyRxRFl8vF83yb/93lcvn9fiojgRooIXu2bt2al5fXpm+iKBYWFv7888+0pgLFUELG1NbWzpkzx+v1tv8lj8cTFxf3/Plz/acCNVBCxiQkJDQ0NHT2q01NTQsWLNBzHlAPJWTJgwcPUlJSBEHo7DcIgrBu3bq8vDw9pwKVUEKWuFwuq9Ua+PdYLJaoqCh95gEiUEJmZGZmnj59uuWxRGe8Xu/p06ePHz+uz1SgHkrIBq/XGx4eLoqinN8siuKUKVO6rCsYBErIhnXr1j1+/Fjmp5YkSaqoqEhJSdF6KiACJWRAZWVlQkJCgNdj2hMEYe7cueXl5dpNBaSghAyIj4/3+Xzd/VOiKM6dO1eLeYAslNDosrKytm3b1q3LYDNBELZt23b9+nUtpgKCUEKjc7lcFovCvyaLxeJyubD/wuBQQkNLT0+/evWqgnvRZj6f79atW/v27SM7FZCFEhpXU1NTTEyMyg9G+Hy+yMjIxsZGUlMBcSihcSUlJVVXV6u8mZQkqaamZvXq1aSmAuJQQoMqKytbsmSJgtdj2hMEYdGiRSUlJeqjQAsooUHNnDmT7Asqs2fPJpgGBKGERnTp0qU9e/YQuQw283g8e/bsOX/+PKlAIAglNBxRFJ1OZ/vtFeo5nU7svzAglNBwpkyZkpWVRbwtoijm5OS4XC6ysaAeNnAbS319fWpqqt1u//zzz7v86GC3+P3+jIyMnTt3rl69umfPngSTQSWU0FgWL14sSRLP82+++eayZcsIJsfHx2dmZkqSlJiYSDYZVOIdDseWLVtojwEcx3GFhYVjxoxpXuJks9lyc3NHjx5t8GRQKTw8HD8TGkhMTEzL6zE8zzd/2YvBk0E9lNAoTp06dfz48ZaPw3u93uPHj2dkZBg5GYhACQ3B7/dHRES0eTovSVJERITid29rnQykoISGsHnz5qKiojYrZERRLC0t3bp1qzGTgRS8MEPfs2fPhg8fXltb2+Gv9urVq6ioqF+/foZKBlLwwowh/PDDDwHeoebxeObPn2+0ZCAIJaTs7t27mzZtCrxUe8OGDdnZ2cZJBrJQQsqioqK63F5hsVgiIiKMkwxkoYQ0HTly5K+//urwK5Za8/l8Fy9ePHr0qBGSgTiUkBqPxxMdHS3zjdp+v9/lcsn8cJN2yaAFlJCaNWvWlJeXy1+qXVlZuXbtWrrJoAU8oqCjoqJixIgR3d2/FBYWVlBQ8Nprr1FJBi3gEQU1s2fPVvCJQVEU58yZQysZNIISUnDz5s0dO3YoW6q9Y8eOa9eu6Z8M2kEJKVCzVJvneafT2dnPe9olg3ZQQr399ttvN27cUPzmab/ff/v27T179uiZDJpCCXXldrtjY2NVfnzB5/NFR0c3NDTokwxaQwl1tXz58mfPnqlfql1bW7ty5Up9kkFzDodDAl2Ulpb26NGD1F+c3W4vLi7WOhm05nA4cCXUT2xsrET0ZY9Zs2ZpnQw6QAl1cvHixf3797fsmFDP4/Hs27fv3Llz2iWTCoTAsPJQDz6fT4ul2s0PFWw2m0bJWVlZNhv+DdEcroR6CA8Pv337NvGl2n6/Pzc3V7vkKVOmkI2FDuH/5zRXW1u7e/duu93+2WefEb+w3Llzh+O4t956i2ysz+fLzMxMS0tLTk7u3bs32XBoAyXUXGJioiRJPM+PHTs2KSmJ9jiyxMXFnTx5UpKkRYsWsTIzu3A7qq2CgoLk5GRBEARBSE5Ovn//Pu2JusbizExDCbUVHR3d8mZOi8Uybdo0uvPIweLMTEMJNXTq1KmMjIzWq68zMjL+/PNPulMFxuLMrEMJtdK8NqLNM3RRFCMjI7tc/UILizObAEqolQ0bNhQXF7dZfS1JUmlp6ebNm2lNFRiLM5sA1lto4tmzZ8OGDaurq+vwV3v27FlUVNS/f3+dpwqMxZlNAOsttDJnzpwA7yPzer0JCQl6ziMHizObA0pIXm5u7tatWwOvvt68eXPzc3aDYHFm00AJyZOz+tpqtRpq9TWLM5sGSkjYwYMH5ay+9nq9ly5dOnTokD5TBcbizGaCEpLk8XhiYmLavLrYGb/fHxkZSX31NYszmwxKSNKqVauePHki8/O1kiQ9ffo0OTlZ66kCY3Fmk8EjCmKePHkyYsQIt9vdrT8VGhpaWFhIa/U1izObDB5RkBQXFyfzpq41SZK+++47LeaRg8WZzQclJOPGjRupqanKVl+npqZeuXJFi6kCY3FmU0IJCZAkSeXq6/bv2NQaizObFUpIQFpamsrV13fu3Nm9ezfZqQJjcWazQgnVcrvdM2bMULnlxev1xsTE6Lb6msWZTQwlVGvp0qXPnz9Xf2NWV1e3YsUKIiN1icWZzQwbuNUoKSmx2+2k/i5CQkKKioowc1DBBm61yK5+4Hl+xowZBAM7xOLM5oYSKnfhwoWDBw+SXX198ODBs2fPkgpsj8WZTQ8lVEjTpdoa7ZJgceZggBIqNHny5OzsbC1WX9+7d+/bb78lG9uMxZmDAd47qkRNTc2AAQNEUZw4cSLBFzk4jvN4PGlpaRaLpby8/KWXXiKYzOLMwSA8PBwbuJVYuHAhx3E8z/ft23fVqlUEk2fMmGGxWCRJWrhwIdlkFmcOFnhE0V35+fktXylhtVrv3buHZC2SgwQeUSgRGRnZekH11KlTkaxFchDBlbBbMjMz27zp2Wq1Hj9+HMlkk4OHw+FACbvB6/WOHj26zb92PM+PHDnS4/EgmVRyUMHtaPekpKQ8fPiw/YLqsrKyDRs2IJlUctDBlVCmqqqqXr16dXaMPXv2rKysRLL65GCDK2E3xMfHB15Q/f333yNZfXIwwpVQjuzsbKvVGvgkLRZLVlYWktUkByFcCeVq/UJ8ZywWi8vlQrKa5CCFK2GX9u7d2/I8OjCbzbZv3z4kK0sOTnhE0bWmpqYhQ4bI/OQBz/MDBw50u91I7m5y0MLtaNeSkpIqKiok2Quqq6qqZL5/EsnwL1wJAygvLw8LC+vukYaGhpaVlSFZfnIww5WwC9OnT1e2oDouLg7J8pODHa6Enbl27Zri3bgWi+XSpUtIlpMc5PDCTKdEUXzvvfdkvgzYntVqfffdd0VRRHLgZMDtaKd27tyZlZWlZkF1dnZ2amoqkgMnA8fhdrQjDQ0Nr7zyivqFSP3796+rq0NyZ8kg4UrYmcWLF9fU1EgkFlQvW7YMyZ0lw79wJWzj4cOHZBdUFxYWIrl9MjTDlbAD0dHRZANbNl4jmSO9/9skcCVs7fTp011+PqC7rFZrZmYmklsn0/57NhCHw4GVh//xeDxOp1Mi/cWXkiQ5nU6bzYbkluScnJwePXqQTWYXSvifb775Ji8vj3isKIoFBQXEY5lOnjx5Mp5YtEAJ//X8+fN9+/bZ7XbiC6o5jrt69SrHce+//z7ZWBaTm9d17927NyUlpU+fPmTDGYUS/mv+/Pkcx/E837t37zVr1tAex7SmTZvW/Mhx3rx5OOdmeHWU4zju3r17KSkpgiAIgrB+/fqcnBzaE5kTzrlDKCHHcVxUVFTrNdIRERF05zErnHOHUELujz/+OHPmTMvX63m93r///vvYsWN0pzIfnHNngr2EXq83Kiqqzcfk/H6/0+kUBIHWVOaDcw4g2Eu4du3ax48ft3kaJklSRUVFSkoKranMB+ccQFB/SWhFRcXIkSMbGho6/NWwsLDCwsIBAwboPJX54JwDCA8PD+orYXx8fIDPyImiiDXSROCcAwveEt66dWv79u0BfiARBGH79u3Xr1/XcyrzwTl3KXhL6HK55KyR1uLdpEEF59ylIC3hnj17rl271uW+Bp/Pl5WVtXfvXn2mMh+csxzBWEK32z1t2jS/3y/nN/t8vqioqMbGRq2nMh+cs0zBWMKkpKTq6mqZNz+SJNXU1GCNtAI4Z5mC7hFFWVnZqFGjuvuA2G635+fnDx06VKOpzAfnLFMwPqKYMWOGsj+INdLdgnOWL7hKePHixfT0dAXvk/J4POnp6efPn9diKvPBOXdLEJVQFEWXy6Vmf6bT6VTwZQzBBufcXUFUwh07duTm5sp8sa49URTz8/N37txJdirzwTl3V7CUsL6+fubMmS2fo1HG4/HExsbW1taSmsp8cM4KBEsJExMT6+vr1ec0NjYuWbJEfY5Z4ZyVCIa9owUFBSEhIaROzGaz3b9/n/Y/kxHhnBUIlg3cU6dOVf99Ji14no+NjSWVZiY4Z2XMX8JTp06dOHHC4/GQCvR6vSdOnMjIyCAVaA44Z8VMXkJBEFwul6TBGmmXy9XU1EQ2ll04ZzVMXsJJkybdv3+f+EMnURQLCwu//vprsrHswjmrYeblv9XV1QcPHgwJCZk4cSLZbz4QBCEtLe3AgQPV1dV9+/YlmMwinLNKZi5hQkICz/M8z/fs2ZPsNqGW/ZkJCQnYU4RzVsusjyhyc3NbvtbLYrHcvn3b+MkswjmrZOZHFJGRkRote9YumUU4ZwJMeSU8dOhQm2+3tNlshw8fNnIyi3DO6jkcDhOWUBCEYcOGtXlqzPP84MGDm5qajJnMIpwzEea8Hf3xxx/Ly8uldsueKysrVX4Xl3bJLMI5E2OyK+GTJ09eeOGFzv5hw8LC/vnnH6MlswjnTIoJr4RxcXEBPskmimJ8fLzRklmEcybJTFfCGzduyNkze+XKFeMkswjnTJDZroRylj3zPK/gXY7aJbMI50yWeUq4e/fu69evd7ns2e/33759+/fffzdCMotwzuSZ43a0sbFxwIAB8j/M9vLLL9fX19NNZhHOmTjz3I4uW7bs2bNnkuxblNra2qSkJLrJLMI5a8IEV8LS0lIFb9632+3FxcW0klmEc9aCSa6EsbGxkqIf02fOnEkrmUU4Z62wfiW8cOFCl6+ndcZisZw9e1b/ZBbhnDXC/HtH/X7/22+/3eadvvJZrdY33njD7/frmcwinLN2mL8d3bZt2927dxUve/b7/Q8ePNi+fbueySzCOWuL3SthbW0tkZUHffr0ef78uT7JLMI5a4rtK+GCBQsaGhrU57jd7sTERH2SWYRz1hyjV8IHDx7YbMQW5Fit1ry8PK2TWYRz1hrDV8KWFUBEWCyWmJgYrZNZhHPWA4tXwpMnTyp+Pa0zVqv1xIkT2iXTPjMlcM46YPIRhdvtHj16NMH/E23G8/yIESO0S25sbKR9ct2Dc9aHw+Fgb+/o//73v/z8/JCQEII/UXAcJ0lSUVERx3EaJU+aNCk9PZ1grNZwzrphrIRVVVWHDx+22+1OpzMsLIxs+OXLlzmO++CDD8jGut3uTZs2HT58uKqqql+/fmTDNYJz1hNjJZw7d67FYpEkyePxsLLzp/mDqpIkzZ07d+PGjbTHkQXnrCuGfibMyclhbiUzZtYHizM3Y+wRReuVzFarlYmVzJhZHyzO/B9WroT79+9vv5L5wIEDtOcKBDPrg8WZWzDziEIQhKFDh7ZfyTxo0CC32017uo5hZn2wOHNrzNyOrly58smTJ1K7lcxPnz5NTk6mNVVgmFkfLM7clvGvhOXl5QFeJQ8NDTXgSmbMrA8WZ26DjSvhrFmzAnwPsyRJs2fP1nMeOTCzPlicuQMGvxJev35dzkrmy5cv0570P5hZHyzO3J7Rr4SSJDmdTjkHbZyVzJhZHyzO3BlDl3DXrl03b97sciWzz+e7c+dOWlqaPlMFhpn1weLMnTLs7WhjY+Orr74qfyVz//79qa9kxsyYubsMfTu6ZMmS5sUhMn9/XV3d8uXLNR2pS5hZHyzOHIgxr4QlJSV2u727/ywhISFFRUWYGTMbbeYAjHslnDp1qoI/xfP89OnTiQ8jE2bWB4szd8GAV8Lz58+rWcl85swZzIyZjTNzYEZ876jf7x83bpyalcxjx471+XyYGTMbYeYuGfF29KeffsrLy1OzkrmgoOCXX34hO1VgmFkfLM4si6GuhDU1NaRWMjd/2R1mxswUZ5bDcFfCefPmkVrJvHDhQvU5cmBm9TlysDizXMa5Eubn55NdyXzv3j3MjJlpzSyTsa6EERERZFcyR0dHk0rrDGbGzAQY5EqYmZmpxUrmY8eOYWbMrP/M8hnlEUVjY+OoUaO0WMk8fPjwhoYGzIyZ9Zy5W4yygfvLL78sKCjQYiVzcXHxV199tX//foKxzTBzC8ysEu9wOLZs2UJxgqdPnw4aNIjjOOLLnt1u98aNG3meLysr69+/P8FkzNwaZlYjPDyc/u2ow+EIDQ3t0aMH8UmQjGT9kxVMQrmE2dnZrRcn37p1C8lIZjdZAfol/OSTT0JCQpqPw2azffTRR0hGMrvJClAuYXp6epuftm022969e5GMZBaTlaFZwqampiFDhrRfnDxw4ECVi5ORjGT9kxWj+Y6ZpKSkDhcnV1VVrVq1CslIZitZFSpXwkePHoWGhnY2kt1uLykpQTKSWUlWg9qVMPDiZI7jFC9ORjKS9U9WS/8r4cWLF+XsbD137hySkWz8ZJUovDAjiuI777zT5ftxrVbruHHj/H4/kpFs5GT1KJTw119/bXlEE5jdbt+xYweSkWzkZPX0LmFdXV2/fv3knEWzvn371tTUIBnJxkwmQu8XZpYsWVJfXy//9zc0NCxbtgzJSDZmMjG6XQkLCwtl3hK0ZrPZ8vPzkYxkoyWTouvt6IQJE5RtL58wYQKSkWy0ZFL0K+GpU6fU7GzNyMhAMpKNk0yQTiX0+XxjxoxRs7389ddf93q9SEayEZLJ0qmEGzZsUHBL0Jrdbt+4cSOSkWyEZLL0KGF1dXXv3r3VnEWzXr16PX36FMlIpptMnB6PKBISEgRBUJ/j8XgWLFiAZCTTTdaEplfCu3fvEtwYabVas7OzkYxkWsla0Px29NNPP1XwlKYzISEhH3/8MZKRTCtZC9qW8MiRI8QXJ9tstqNHjyIZyfona1QTDUsoCMLw4cPb7BFQj+f5wYMHDxs2DMlI1jN5yJAhTU1NWjRFww3cKSkpxcXFISEhZE9EkqRHjx5xHIdkJOuZXFpaun79eo2+9V6rEgqCoNHnlC9dusRx3IcffohkJOuWzHEckZdbO6RVCePj4zVKBjAZA30/IUBwQgkBKEMJAShDCQEoQwkBKEMJAShDCQEoQwkBKEMJAShDCQEoQwkBKEMJAShDCQEoQwkBKEMJAShDCQEoQwkBKEMJAShDCQEoQwkBKEMJAShDCQEoQwkBKEMJAShDCQEoQwkBKEMJAShDCQEoQwkBKEMJAShDCQEoQwkBKEMJAShDCQEoQwkBKEMJAShDCQEoQwkBKEMJAShDCQEoQwkBKEMJAShDCQEo48eMGTN+/HjaYwAEqXPnzv0fXuhGu5+B2XIAAAAASUVORK5CYII=" + "image/png": "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" }, "metadata": {}, "output_type": "display_data" @@ -1461,10 +1331,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "Just like with Diagrams, `ihaskell-charts` allows you to use the [Chart](https://github.com/timbod7/haskell-chart/wiki) library for plotting from within IHaskell. (You will need to install `cairo` as well, which may be a bit of a hassle.)" ] @@ -1472,15 +1339,11 @@ { "cell_type": "code", "execution_count": 20, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { - "image/png": "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" + "image/png": "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" }, "metadata": {}, "output_type": "display_data" @@ -1514,10 +1377,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "In addition to displaying outputs in a rich format, IHaskell has a bunch of useful features.\n", "\n", @@ -1527,11 +1387,7 @@ { "cell_type": "code", "execution_count": 21, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -1668,10 +1524,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "If you're an experienced Haskeller, though, and don't want `hlint` telling you what to do, you can easily turn it off:" ] @@ -1679,11 +1532,7 @@ { "cell_type": "code", "execution_count": 22, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "-- If hlint annoys you, though, you can turn it off.\n", @@ -1694,11 +1543,7 @@ { "cell_type": "code", "execution_count": 23, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -1717,10 +1562,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "In addition to `hlint` integration, IHaskell also integrates **Hoogle** for documentation searches. IHaskell provides two directives for searching Hoogle. The first of these, `:document` (or shorthands), looks for exact matches." ] @@ -1728,11 +1570,7 @@ { "cell_type": "code", "execution_count": 24, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": {}, @@ -1823,14 +1661,243 @@ ".suggestion-name {\n", "font-weight: bold;\n", "}\n", - "filterM ∷ Monad m ⇒ (a → m Bool) → [a] → m [a](package base, module Control.Monad)
This generalizes the list-based filter function. \n", + "filterM ∷ Applicative m ⇒ (a → m Bool) → [a] → m [a]
This generalizes the list-based filter function.\n", "
\n", + "filterM ∷ Monad m ⇒ (a → m Bool) → Vector a → m (Vector a)
O(n) Drop elements that do not satisfy the monadic predicate\n", + "
\n", + "filterM ∷ Monad m ⇒ (a → m Bool) → Bundle v a → Bundle m v a
Yield a monadic stream of elements that satisfy the monadic predicate\n", + "
\n", + "filterM ∷ Monad m ⇒ (a → m Bool) → Bundle m v a → Bundle m v a
Drop elements which do not satisfy the monadic predicate\n", + "
\n", + "filterM ∷ Monad m ⇒ (a → m Bool) → Stream m a → Stream m a
Drop elements which do not satisfy the monadic predicate\n", + "
\n", + "filterM ∷ (Monad m, Vector v a) ⇒ (a → m Bool) → v a → m (v a)
O(n) Drop elements that do not satisfy the monadic predicate\n", + "
\n", + "filterM ∷ (Monad m, Prim a) ⇒ (a → m Bool) → Vector a → m (Vector a)
O(n) Drop elements that do not satisfy the monadic predicate\n", + "
\n", + "filterM ∷ (Monad m, Storable a) ⇒ (a → m Bool) → Vector a → m (Vector a)
O(n) Drop elements that do not satisfy the monadic predicate\n", + "
\n", + "filterM ∷ (Monad m, Unbox a) ⇒ (a → m Bool) → Vector a → m (Vector a)
O(n) Drop elements that do not satisfy the monadic predicate\n", + "
\n", + "filterM ∷ Monad m ⇒ (a → m Bool) → ConduitT a a m ()
Keep only values in the stream passing a given monadic predicate.\n", + "\n", + "Subject to fusion\n", + "
\n", + "filterM ∷ Applicative m ⇒ a → m Bool → [a] → m [a]
This generalizes the list-based filter function.\n", + "
\n", + "filterM ∷ Monad m ⇒ (a → m Bool) → Pipe a a m r
(filterM predicate) only forwards values that satisfy the\n", + "monadic predicate\n", + "\n", + "
\n",
+       "filterM (pure (pure True)) = cat\n",
+       "\n",
+       "filterM (liftA2 (liftA2 (&&)) p1 p2) = filterM p1 >-> filterM p2\n",
+       "
\n", + "
\n", + "filterM ∷ (a → IO Bool) → InputStream a → IO (InputStream a)
Drops chunks from an input stream if they fail to match a given filter\n", + "predicate. See filter.\n", + "\n", + "Items pushed back to the returned stream are propagated back upstream.\n", + "\n", + "Example:\n", + "\n", + "
\n",
+       "ghci> Streams.fromList [\"the\", \"quick\", \"brown\", \"fox\"] >>=\n",
+       "Streams.filterM (return . (/= \"brown\")) >>= Streams.toList\n",
+       "[\"the\",\"quick\",\"fox\"]\n",
+       "
\n", + "
\n", + "filterM ∷ Applicative m ⇒ a → m Bool → [a] → m [a]
This generalizes the list-based filter function.\n", + "
\n", + "filterM ∷ Applicative m ⇒ a → m Bool → [a] → m [a]
This generalizes the list-based filter function.\n", + "
\n", + "filterM ∷ (IsSequence seq, Monad m) ⇒ (Element seq → m Bool) → seq → m seq
The monadic version of filter.\n", + "
\n", + "filterM ∷ (Monad m) ⇒ (a → m Bool) → Stream (Of a) m r → Stream (Of a) m r
Skip elements of a stream that fail a monadic test\n", + "
\n", + "filterM ∷ Applicative m ⇒ a → m Bool → [a] → m [a]
This generalizes the list-based filter function.\n", + "
\n", + "filterM ∷ (Monad m, Vector v a) ⇒ a → m Bool → v a → m v a
O(n) Drop elements that do not satisfy the monadic predicate\n", + "
\n", + "filterM ∷ Monad m ⇒ a → m Bool → Vector a → m Vector a
O(n) Drop elements that do not satisfy the monadic predicate\n", + "
\n", + "filterM ∷ (Monad m, Storable a) ⇒ a → m Bool → Vector a → m Vector a
O(n) Drop elements that do not satisfy the monadic predicate\n", + "
\n", + "filterM ∷ (Monad m, Unbox a) ⇒ a → m Bool → Vector a → m Vector a
O(n) Drop elements that do not satisfy the monadic predicate\n", + "
\n", + "filterM ∷ Applicative m ⇒ a → m Bool → [a] → m [a]
This generalizes the list-based filter function.\n", + "
\n", + "filterM ∷ Monad m ⇒ (a → m Bool) → [a] → m [a]
\n", + "filterM ∷ Applicative m ⇒ a → m Bool → [a] → m [a]
This generalizes the list-based filter function.\n", + "
\n", + "filterM ∷ Applicative m ⇒ a → m Bool → [a] → m [a]
This generalizes the list-based filter function.\n", + "
\n", + "filterM ∷ (Applicative f) ⇒ (a → f Bool) → [a] → f [a]
See mapM.\n", + "
\n", + "filterM ∷ (Monad m, Vector u a, Vector v b) ⇒ ((a, b) → m Bool) → Vector u v (a, b) → m (Vector u v (a, b))
O(n) Drop elements that do not satisfy the monadic predicate\n", + "
\n", + "filterM ∷ (IsStream t, Monad m) ⇒ (a → m Bool) → t m a → t m a
Same as filter but with a monadic predicate.\n", + "
\n", + "filterM ∷ Applicative m ⇒ a → m Bool → [a] → m [a]
This generalizes the list-based filter function.\n", + "
\n", + "filterM ∷ Applicative m ⇒ a → m Bool → [a] → m [a]
This generalizes the list-based filter function.\n", + "
\n", + "filterM ∷ Applicative m ⇒ a → m Bool → [a] → m [a]
This generalizes the list-based filter function.\n", + "
\n", + "filterM ∷ Applicative m ⇒ a → m Bool → [a] → m [a]
This generalizes the list-based filter function.\n", "
\n" ], "text/plain": [ + "filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]\n", + "URL: https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:filterM\n", + "This generalizes the list-based filter function.\n", + "\n", + "filterM :: Monad m => (a -> m Bool) -> Vector a -> m (Vector a)\n", + "URL: https://hackage.haskell.org/package/vector/docs/Data-Vector.html#v:filterM\n", + "O(n) Drop elements that do not satisfy the monadic predicate\n", + "\n", + "filterM :: Monad m => (a -> m Bool) -> Bundle v a -> Bundle m v a\n", + "URL: https://hackage.haskell.org/package/vector/docs/Data-Vector-Fusion-Bundle.html#v:filterM\n", + "Yield a monadic stream of elements that satisfy the monadic predicate\n", + "\n", + "filterM :: Monad m => (a -> m Bool) -> Bundle m v a -> Bundle m v a\n", + "URL: https://hackage.haskell.org/package/vector/docs/Data-Vector-Fusion-Bundle-Monadic.html#v:filterM\n", + "Drop elements which do not satisfy the monadic predicate\n", + "\n", + "filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a\n", + "URL: https://hackage.haskell.org/package/vector/docs/Data-Vector-Fusion-Stream-Monadic.html#v:filterM\n", + "Drop elements which do not satisfy the monadic predicate\n", + "\n", + "filterM :: (Monad m, Vector v a) => (a -> m Bool) -> v a -> m (v a)\n", + "URL: https://hackage.haskell.org/package/vector/docs/Data-Vector-Generic.html#v:filterM\n", + "O(n) Drop elements that do not satisfy the monadic predicate\n", + "\n", + "filterM :: (Monad m, Prim a) => (a -> m Bool) -> Vector a -> m (Vector a)\n", + "URL: https://hackage.haskell.org/package/vector/docs/Data-Vector-Primitive.html#v:filterM\n", + "O(n) Drop elements that do not satisfy the monadic predicate\n", + "\n", + "filterM :: (Monad m, Storable a) => (a -> m Bool) -> Vector a -> m (Vector a)\n", + "URL: https://hackage.haskell.org/package/vector/docs/Data-Vector-Storable.html#v:filterM\n", + "O(n) Drop elements that do not satisfy the monadic predicate\n", + "\n", + "filterM :: (Monad m, Unbox a) => (a -> m Bool) -> Vector a -> m (Vector a)\n", + "URL: https://hackage.haskell.org/package/vector/docs/Data-Vector-Unboxed.html#v:filterM\n", + "O(n) Drop elements that do not satisfy the monadic predicate\n", + "\n", + "filterM :: Monad m => (a -> m Bool) -> ConduitT a a m ()\n", + "URL: https://hackage.haskell.org/package/conduit/docs/Data-Conduit-Combinators.html#v:filterM\n", + "Keep only values in the stream passing a given monadic predicate.\n", + "\n", + "Subject to fusion\n", + "\n", + "filterM :: Applicative m => a -> m Bool -> [a] -> m [a]\n", + "URL: https://hackage.haskell.org/package/Cabal/docs/Distribution-Compat-Prelude-Internal.html#v:filterM\n", + "This generalizes the list-based filter function.\n", + "\n", + "filterM :: Monad m => (a -> m Bool) -> Pipe a a m r\n", + "URL: https://hackage.haskell.org/package/pipes/docs/Pipes-Prelude.html#v:filterM\n", + "(filterM predicate) only forwards values that satisfy the\n", + "monadic predicate\n", + "\n", + "
\n",
+       "filterM (pure (pure True)) = cat\n",
+       "\n",
+       "filterM (liftA2 (liftA2 (&&)) p1 p2) = filterM p1 >-> filterM p2\n",
+       "
\n", + "\n", + "filterM :: (a -> IO Bool) -> InputStream a -> IO (InputStream a)\n", + "URL: https://hackage.haskell.org/package/io-streams/docs/System-IO-Streams-Combinators.html#v:filterM\n", + "Drops chunks from an input stream if they fail to match a given filter\n", + "predicate. See filter.\n", + "\n", + "Items pushed back to the returned stream are propagated back upstream.\n", + "\n", + "Example:\n", + "\n", + "
\n",
+       "ghci> Streams.fromList [\"the\", \"quick\", \"brown\", \"fox\"] >>=\n",
+       "Streams.filterM (return . (/= \"brown\")) >>= Streams.toList\n",
+       "[\"the\",\"quick\",\"fox\"]\n",
+       "
\n", + "\n", + "filterM :: Applicative m => a -> m Bool -> [a] -> m [a]\n", + "URL: https://hackage.haskell.org/package/protolude/docs/Protolude.html#v:filterM\n", + "This generalizes the list-based filter function.\n", + "\n", + "filterM :: Applicative m => a -> m Bool -> [a] -> m [a]\n", + "URL: https://hackage.haskell.org/package/protolude/docs/Protolude-Monad.html#v:filterM\n", + "This generalizes the list-based filter function.\n", + "\n", + "filterM :: (IsSequence seq, Monad m) => (Element seq -> m Bool) -> seq -> m seq\n", + "URL: https://hackage.haskell.org/package/mono-traversable/docs/Data-Sequences.html#v:filterM\n", + "The monadic version of filter.\n", + "\n", + "filterM :: (Monad m) => (a -> m Bool) -> Stream (Of a) m r -> Stream (Of a) m r\n", + "URL: https://hackage.haskell.org/package/streaming/docs/Streaming-Prelude.html#v:filterM\n", + "Skip elements of a stream that fail a monadic test\n", + "\n", + "filterM :: Applicative m => a -> m Bool -> [a] -> m [a]\n", + "URL: https://hackage.haskell.org/package/rio/docs/RIO.html#v:filterM\n", + "This generalizes the list-based filter function.\n", + "\n", + "filterM :: (Monad m, Vector v a) => a -> m Bool -> v a -> m v a\n", + "URL: https://hackage.haskell.org/package/rio/docs/RIO-Vector.html#v:filterM\n", + "O(n) Drop elements that do not satisfy the monadic predicate\n", + "\n", + "filterM :: Monad m => a -> m Bool -> Vector a -> m Vector a\n", + "URL: https://hackage.haskell.org/package/rio/docs/RIO-Vector-Boxed.html#v:filterM\n", + "O(n) Drop elements that do not satisfy the monadic predicate\n", + "\n", + "filterM :: (Monad m, Storable a) => a -> m Bool -> Vector a -> m Vector a\n", + "URL: https://hackage.haskell.org/package/rio/docs/RIO-Vector-Storable.html#v:filterM\n", + "O(n) Drop elements that do not satisfy the monadic predicate\n", + "\n", + "filterM :: (Monad m, Unbox a) => a -> m Bool -> Vector a -> m Vector a\n", + "URL: https://hackage.haskell.org/package/rio/docs/RIO-Vector-Unboxed.html#v:filterM\n", + "O(n) Drop elements that do not satisfy the monadic predicate\n", + "\n", + "filterM :: Applicative m => a -> m Bool -> [a] -> m [a]\n", + "URL: https://hackage.haskell.org/package/rebase/docs/Rebase-Prelude.html#v:filterM\n", + "This generalizes the list-based filter function.\n", + "\n", "filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]\n", - "URL: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Monad.html#v:filterM\n", - "This generalizes the list-based filter function." + "URL: https://hackage.haskell.org/package/yjtools/docs/Control-Monad-Tools.html#v:filterM\n", + "\n", + "filterM :: Applicative m => a -> m Bool -> [a] -> m [a]\n", + "URL: https://hackage.haskell.org/package/llvm-hs-pure/docs/LLVM-Prelude.html#v:filterM\n", + "This generalizes the list-based filter function.\n", + "\n", + "filterM :: Applicative m => a -> m Bool -> [a] -> m [a]\n", + "URL: https://hackage.haskell.org/package/universum/docs/Universum-Monad-Reexport.html#v:filterM\n", + "This generalizes the list-based filter function.\n", + "\n", + "filterM :: (Applicative f) => (a -> f Bool) -> [a] -> f [a]\n", + "URL: https://hackage.haskell.org/package/haxl/docs/Haxl-Prelude.html#v:filterM\n", + "See mapM.\n", + "\n", + "filterM :: (Monad m, Vector u a, Vector v b) => ((a, b) -> m Bool) -> Vector u v (a, b) -> m (Vector u v (a, b))\n", + "URL: https://hackage.haskell.org/package/hybrid-vectors/docs/Data-Vector-Hybrid.html#v:filterM\n", + "O(n) Drop elements that do not satisfy the monadic predicate\n", + "\n", + "filterM :: (IsStream t, Monad m) => (a -> m Bool) -> t m a -> t m a\n", + "URL: https://hackage.haskell.org/package/streamly/docs/Streamly-Prelude.html#v:filterM\n", + "Same as filter but with a monadic predicate.\n", + "\n", + "filterM :: Applicative m => a -> m Bool -> [a] -> m [a]\n", + "URL: https://hackage.haskell.org/package/yesod-paginator/docs/Yesod-Paginator-Prelude.html#v:filterM\n", + "This generalizes the list-based filter function.\n", + "\n", + "filterM :: Applicative m => a -> m Bool -> [a] -> m [a]\n", + "URL: https://hackage.haskell.org/package/control-monad-free/docs/Control-Monad-Free.html#v:filterM\n", + "This generalizes the list-based filter function.\n", + "\n", + "filterM :: Applicative m => a -> m Bool -> [a] -> m [a]\n", + "URL: https://hackage.haskell.org/package/hledger-web/docs/Hledger-Web-Import.html#v:filterM\n", + "This generalizes the list-based filter function.\n", + "\n", + "filterM :: Applicative m => a -> m Bool -> [a] -> m [a]\n", + "URL: https://hackage.haskell.org/package/relude/docs/Relude-Monad-Reexport.html#v:filterM\n", + "This generalizes the list-based filter function." ] }, "metadata": {}, @@ -1843,10 +1910,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "The other provided command is `:hoogle`. This does a normal Hoogle search, and thus lets you use imperfect matching and searching by type signature. This will show you documentation for things that match the desired type signature, as demonstrated below. It automatically formats inline Haskell code and hyperlinks the identifiers to their respective Haddock documentations." ] @@ -1854,11 +1918,7 @@ { "cell_type": "code", "execution_count": 25, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": {}, @@ -1949,281 +2009,1187 @@ ".suggestion-name {\n", "font-weight: bold;\n", "}\n", - "zip ∷ [a] → [b] → [(a, b)](package base, module Prelude)
zip takes two lists and returns a list of corresponding pairs. If one input list is short, excess elements of the longer list are discarded. \n", + "zip ∷ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "\n", + "
\n",
+       "zip [1, 2] ['a', 'b'] = [(1, 'a'), (2, 'b')]\n",
+       "
\n", + "\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded:\n", + "\n", + "
\n",
+       "zip [1] ['a', 'b'] = [(1, 'a')]\n",
+       "zip [1, 2] ['a'] = [(1, 'a')]\n",
+       "
\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "zip _|_ [] = _|_\n",
+       "
\n", "
\n", + "zip ∷ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "\n", + "
\n",
+       "zip [1, 2] ['a', 'b'] = [(1, 'a'), (2, 'b')]\n",
+       "
\n", + "\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded:\n", + "\n", + "
\n",
+       "zip [1] ['a', 'b'] = [(1, 'a')]\n",
+       "zip [1, 2] ['a'] = [(1, 'a')]\n",
+       "
\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "zip _|_ [] = _|_\n",
+       "
\n", "
\n", - "(>*<) ∷ Monoidal f ⇒ f a → f b → f (a, b)(package bytestring, module Data.ByteString.Builder.Prim)
A pairing/concatenation operator for builder primitives, both bounded and fixed size.\n", + "zip ∷ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "\n", + "
\n",
+       "zip [1, 2] ['a', 'b'] = [(1, 'a'), (2, 'b')]\n",
+       "
\n", + "\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded:\n", + "\n", + "
\n",
+       "zip [1] ['a', 'b'] = [(1, 'a')]\n",
+       "zip [1, 2] ['a'] = [(1, 'a')]\n",
+       "
\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "zip _|_ [] = _|_\n",
+       "
\n", "
\n", - "
\n", - "
For example,\n", + "zip ∷ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "\n", + "
\n",
+       "zip [1, 2] ['a', 'b'] = [(1, 'a'), (2, 'b')]\n",
+       "
\n", + "\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded:\n", + "\n", + "
\n",
+       "zip [1] ['a', 'b'] = [(1, 'a')]\n",
+       "zip [1, 2] ['a'] = [(1, 'a')]\n",
+       "
\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "zip _|_ [] = _|_\n",
+       "
\n", "
\n", - "
\n", - "
> toLazyByteString (primFixed (char7 >*< char7) ('x','y')) = \"xy\"\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
We can combine multiple primitives using >*< multiple times.\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
> toLazyByteString (primFixed (char7 >*< char7 >*< char7) ('x',('y','z'))) = \"xyz\" \n", + "zipExact ∷ Partial ⇒ [a] → [b] → [(a, b)]
\n",
+       "zipExact xs ys =\n",
+       "| length xs == length ys = zip xs ys\n",
+       "| otherwise              = error \"some message\"\n",
+       "
\n", "
\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "shrinkState ∷ ShrinkState s a ⇒ a → s → [(a, s)](package QuickCheck, module Test.QuickCheck.Modifiers)
\n", - "breakOnAll ∷ Text → Text → [(Text, Text)](package text, module Data.Text)
O(n+m) Find all non-overlapping instances of needle in haystack. Each element of the returned list consists of a pair:\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
* The entire string prior to the kth match (i.e. the prefix)\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
* The kth match, followed by the remainder of the string\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
Examples:\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
> breakOnAll \"::\" \"\"\n", - "> ==> []\n", - "> breakOnAll \"/\" \"a/b/c/\"\n", - "> ==> [(\"a\", \"/b/c/\"), (\"a/b\", \"/c/\"), (\"a/b/c\", \"/\")]\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
In (unlikely) bad cases, this function's time complexity degrades towards O(n*m).\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
The needle parameter may not be empty. \n", + "zip ∷ [a] → [b] → [(a, b)]
\n", + "(+*+) ∷ [a] → [b] → [(a, b)]
Slightly unfair 2-way Cartesian product: given two (possibly infinite)\n", + "lists, produce a single list such that whenever v and\n", + "w have finite indices in the input lists, (v,w) has\n", + "finite index in the output list. Lower indices occur as the\n", + "fst part of the tuple more frequently, but not exponentially\n", + "so.\n", "
\n", + "unfairCartesianProduct ∷ [a] → [b] → [(a, b)]
Very unfair 2-way Cartesian product: same guarantee as the slightly\n", + "unfair one, except that lower indices may occur as the fst\n", + "part of the tuple exponentially more frequently. This mainly exists as\n", + "a specification to test against.\n", "
\n", - "breakOnAll ∷ Text → Text → [(Text, Text)](package text, module Data.Text.Lazy)
O(n+m) Find all non-overlapping instances of needle in haystack. Each element of the returned list consists of a pair:\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
* The entire string prior to the kth match (i.e. the prefix)\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
* The kth match, followed by the remainder of the string\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
Examples:\n", + "zip ∷ [a] → [b] → [(a, b)]
\n", + "zip ∷ [a] → [b] → [(a, b)]
\n", + "zip ∷ [a] → [b] → [(a, b)]
\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
> breakOnAll \"::\" \"\"\n", - "> ==> []\n", - "> breakOnAll \"/\" \"a/b/c/\"\n", - "> ==> [(\"a\", \"/b/c/\"), (\"a/b\", \"/c/\"), (\"a/b/c\", \"/\")]\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
This function is strict in its first argument, and lazy in its second.\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
In (unlikely) bad cases, this function's time complexity degrades towards O(n*m).\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "
\n", - "
The needle parameter may not be empty. \n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", + "zip ∷ () ⇒ [a] → [b] → [(a, b)]
zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", "
\n", - "genLNodes ∷ Enum a ⇒ a → Int → [LNode a](package fgl, module Data.Graph.Inductive.Example)
generate list of labeled nodes \n", + "zipLazy ∷ [a] → [b] → [(a, b)]
zipLazy is a kind of zip that is lazy in the second list\n", + "(observe the ~)\n", "
\n", + "concurrently ∷ MonadBaseControl IO m ⇒ m a → m b → m (a, b)
Generalized version of concurrently.\n", "
\n", - "gmapAccumT ∷ Data d ⇒ (∀ e. Data e ⇒ a → e → (a, e)) → a → d → (a, d)(package syb, module Data.Generics.Twins)
gmapT with accumulation \n", + "concurrently ∷ ∀ m a b . (MonadBaseControl IO m, Forall (Pure m)) ⇒ m a → m b → m (a, b)
Generalized version of concurrently.\n", "
\n", + "pairADefault ∷ Applicative f ⇒ f a → f b → f (a, b)
Default '>*< implementation for non-invertible\n", + "Applicatives.\n", "
\n", - "threadList ∷ (Collect r c) → (Split t i r) → [i] → t → (c, t)(package fgl, module Data.Graph.Inductive.Internal.Thread)
\n", - "threadList' ∷ (Collect r c) → (Split t i r) → [i] → t → (c, t)(package fgl, module Data.Graph.Inductive.Internal.Thread)
\n", - "mapAccumL ∷ (acc → Word8 → (acc, Word8)) → acc → ByteString → (acc, ByteString)(package bytestring, module Data.ByteString.Lazy)
The mapAccumL function behaves like a combination of map and foldl; it applies a function to each element of a ByteString, passing an accumulating parameter from left to right, and returning a final value of this accumulator together with the new ByteString. \n", + "zip ∷ (Vector v a, Vector v b, Vector v (a, b)) ⇒ v a → v b → v (a, b)
O(min(m,n)) Zip two vectors\n", "
\n", + "pair ∷ (Vector v a, Vector v b, Vector v (a, b)) ⇒ v a → v b → v (a, b)
Pair two samples. It's like zip but requires that both samples\n", + "have equal size.\n", "
\n", - "mapAccumL ∷ (acc → Word8 → (acc, Word8)) → acc → ByteString → (acc, ByteString)(package bytestring, module Data.ByteString)
The mapAccumL function behaves like a combination of map and foldl; it applies a function to each element of a ByteString, passing an accumulating parameter from left to right, and returning a final value of this accumulator together with the new list. \n", + "zip ∷ (Vector v a, Vector v b, Vector v (a, b)) ⇒ v a → v b → v (a, b)
O(min(m,n)) Zip two vectors\n", "
\n", + "concurrently ∷ MonadUnliftIO m ⇒ m a → m b → m (a, b)
Unlifted concurrently.\n", "
\n", - "mapAccumR ∷ (acc → Word8 → (acc, Word8)) → acc → ByteString → (acc, ByteString)(package bytestring, module Data.ByteString)
The mapAccumR function behaves like a combination of map and foldr; it applies a function to each element of a ByteString, passing an accumulating parameter from right to left, and returning a final value of this accumulator together with the new ByteString. \n", + "concurrently ∷ MonadUnliftIO m ⇒ m a → m b → m (a, b)
Unlifted concurrently.\n", "
\n", + "zip ∷ Sequence s ⇒ s a → s b → s (a, b)
Combine two sequences into a sequence of pairs. If the sequences are\n", + "different lengths, the excess elements of the longer sequence is\n", + "discarded.\n", + "\n", + "
\n",
+       "zip <x0,...,xn-1> <y0,...,ym-1> = <(x0,y0),...,(xj-1,yj-1)>\n",
+       "where j = min {n,m}\n",
+       "
\n", + "\n", + "Axioms:\n", + "\n", + "
    \n", + "
  • zip xs ys = zipWith (,) xs ys
  • \n", + "
\n", + "\n", + "This function is always unambiguous.\n", + "\n", + "Default running time: O( min( n1, n2 ) )\n", "
\n", - "mapAccumL ∷ (acc → Char → (acc, Char)) → acc → ByteString → (acc, ByteString)(package bytestring, module Data.ByteString.Lazy.Char8)
The mapAccumL function behaves like a combination of map and foldl; it applies a function to each element of a ByteString, passing an accumulating parameter from left to right, and returning a final value of this accumulator together with the new ByteString. \n", + "zipUsingLview ∷ Sequence s ⇒ s a → s b → s (a, b)
\n", + "zipUsingLists ∷ Sequence s ⇒ s a → s b → s (a, b)
\n", + "concurrently ∷ MonadConc m ⇒ m a → m b → m (a, b)
Run two MonadConc actions concurrently, and return both\n", + "results. If either action throws an exception at any time, then the\n", + "other action is cancelled, and the exception is re-thrown by\n", + "concurrently.\n", + "\n", + "
\n",
+       "concurrently left right =\n",
+       "withAsync left $ \\a ->\n",
+       "withAsync right $ \\b ->\n",
+       "waitBoth a b\n",
+       "
\n", "
\n", + "mzipRep ∷ Representable f ⇒ f a → f b → f (a, b)
\n", + "box ∷ Graph g ⇒ g a → g b → g (a, b)
Compute the Cartesian product of graphs. Complexity: O(s1 *\n", + "s2) time, memory and size, where s1 and s2 are the\n", + "sizes of the given graphs.\n", + "\n", + "
\n",
+       "box (path [0,1]) (path \"ab\") == edges [ ((0,'a'), (0,'b'))\n",
+       ", ((0,'a'), (1,'a'))\n",
+       ", ((0,'b'), (1,'b'))\n",
+       ", ((1,'a'), (1,'b')) ]\n",
+       "
\n", + "\n", + "Up to an isomorphism between the resulting vertex types, this\n", + "operation is commutative, associative,\n", + "distributes over overlay, has singleton graphs as\n", + "identities and empty as the annihilating zero.\n", + "Below ~~ stands for the equality up to an isomorphism, e.g.\n", + "(x, ()) ~~ x.\n", + "\n", + "
\n",
+       "box x y               ~~ box y x\n",
+       "box x (box y z)       ~~ box (box x y) z\n",
+       "box x (overlay y z)   == overlay (box x y) (box x z)\n",
+       "box x (vertex ())     ~~ x\n",
+       "box x empty           ~~ empty\n",
+       "vertexCount (box x y) == vertexCount x * vertexCount y\n",
+       "edgeCount   (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y\n",
+       "
\n", "
\n", - "mapAccumL ∷ (acc → Char → (acc, Char)) → acc → ByteString → (acc, ByteString)(package bytestring, module Data.ByteString.Char8)
The mapAccumL function behaves like a combination of map and foldl; it applies a function to each element of a ByteString, passing an accumulating parameter from left to right, and returning a final value of this accumulator together with the new list. \n", + "divided ∷ Divisible f ⇒ f a → f b → f (a, b)
\n",
+       "divided = divide id\n",
+       "
\n", "
\n", + "(>*<) ∷ Divisible f ⇒ f a → f b → f (a, b)
The RecordInputType divisible (contravariant) functor allows\n", + "you to build an InputType injector for a Dhall record.\n", + "\n", + "For example, let's take the following Haskell data type:\n", + "\n", + "
\n",
+       "data Project = Project\n",
+       "{ projectName :: Text\n",
+       ", projectDescription :: Text\n",
+       ", projectStars :: Natural\n",
+       "}\n",
+       "
\n", + "\n", + "And assume that we have the following Dhall record that we would like\n", + "to parse as a Project:\n", + "\n", + "
\n",
+       "{ name =\n",
+       "\"dhall-haskell\"\n",
+       ", description =\n",
+       "\"A configuration language guaranteed to terminate\"\n",
+       ", stars =\n",
+       "289\n",
+       "}\n",
+       "
\n", + "\n", + "Our injector has type InputType Project, but we can't\n", + "build that out of any smaller injectors, as InputTypes cannot\n", + "be combined (they are only Contravariants). However, we can use\n", + "an InputRecordType to build an InputType for\n", + "Project:\n", + "\n", + "
\n",
+       "injectProject :: InputType Project\n",
+       "injectProject =\n",
+       "inputRecord\n",
+       "(  adapt >$< inputFieldWith \"name\" inject\n",
+       ">*< inputFieldWith \"description\" inject\n",
+       ">*< inputFieldWith \"stars\" inject\n",
+       ")\n",
+       "where\n",
+       "adapt (Project{..}) = (projectName, (projectDescription, projectStars))\n",
+       "
\n", + "\n", + "Or, since we are simply using the Inject instance to inject\n", + "each field, we could write\n", + "\n", + "
\n",
+       "injectProject :: InputType Project\n",
+       "injectProject =\n",
+       "inputRecord\n",
+       "(  adapt >$< inputField \"name\"\n",
+       ">*< inputField \"description\"\n",
+       ">*< inputField \"stars\"\n",
+       ")\n",
+       "where\n",
+       "adapt (Project{..}) = (projectName, (projectDescription, projectStars))\n",
+       "
\n", + "\n", + "Infix divided\n", "
\n", - "mapAccumR ∷ (acc → Char → (acc, Char)) → acc → ByteString → (acc, ByteString)(package bytestring, module Data.ByteString.Char8)
The mapAccumR function behaves like a combination of map and foldr; it applies a function to each element of a ByteString, passing an accumulating parameter from right to left, and returning a final value of this accumulator together with the new ByteString. \n", + "divided ∷ Divisible f ⇒ f a → f b → f (a, b)
\n",
+       "divided = divide id\n",
+       "
\n", "
\n", + "(>*<) ∷ Divisible f ⇒ f a → f b → f (a, b)
An alias to divided.\n", "
\n", - "mapAccumL ∷ (a → Char → (a, Char)) → a → Text → (a, Text)(package text, module Data.Text)
O(n) Like a combination of map and foldl'. Applies a function to each element of a Text, passing an accumulating parameter from left to right, and returns a final Text. Performs replacement on invalid scalar values. \n", + "(>*<) ∷ Divisible f ⇒ f a → f b → f (a, b)
An alias to divided.\n", "
\n", + "contrazip2 ∷ Divisible f ⇒ f a1 → f a2 → f (a1, a2)
\n", + "contrazip2 ∷ ∀ f a1 a2 . Divisible f ⇒ f a1 → f a2 → f (a1, a2)
\n", + "pair ∷ Sized f ⇒ f a → f b → f (a, b)
Default: pair a b = (,) $ a * b.\n", "
\n", - "mapAccumR ∷ (a → Char → (a, Char)) → a → Text → (a, Text)(package text, module Data.Text)
The mapAccumR function behaves like a combination of map and a strict foldr; it applies a function to each element of a Text, passing an accumulating parameter from right to left, and returning a final value of this accumulator together with the new Text. Performs replacement on invalid scalar values. \n", - "
\n", - "
\n", - "execRWS ∷ RWS r w s a → r → s → (s, w)(package transformers, module Control.Monad.Trans.RWS.Lazy)
Evaluate a computation with the given initial state and environment, returning the final state and output, discarding the final value. \n", - "
\n", - "
\n", - "breakSubstring ∷ ByteString → ByteString → (ByteString, ByteString)(package bytestring, module Data.ByteString)
otherwise -> Just (length x) \n", - "
\n", - "
\n", - "
For example, to tokenise a string, dropping delimiters:\n", - "
\n", - "
\n", - "
> tokenise x y = h (:) if null t then [] else tokenise x (drop (length x) t)\n", - "> \n", - "
\n", - "
\n", - "
To skip to the first occurence of a string:\n", - "
\n", - "
\n", - "
> snd (breakSubstring x y)\n", - "
\n", - "
\n", - "
To take the parts of a string before a delimiter:\n", - "
\n", - "
\n", - "
> fst (breakSubstring x y) \n", - "
\n", - "
\n", - "breakOn ∷ Text → Text → (Text, Text)(package text, module Data.Text)
O(n+m) Find the first instance of needle (which must be non-null) in haystack. The first element of the returned tuple is the prefix of haystack before needle is matched. The second is the remainder of haystack, starting with the match.\n", - "
\n", - "
\n", - "
Examples:\n", - "
\n", - "
\n", - "
> breakOn \"::\" \"a::b::c\" ==> (\"a\", \"::b::c\")\n", - "> breakOn \"/\" \"foobar\" ==> (\"foobar\", \"\")\n", - "
\n", - "
\n", - "
Laws:\n", - "
\n", - "
\n", - "
> append prefix match == haystack\n", - "> \n", - "
\n", - "
\n", - "
If you need to break a string by a substring repeatedly (e.g. you want to break on every instance of a substring), use breakOnAll instead, as it has lower startup overhead.\n", - "
\n", - "
\n", - "
In (unlikely) bad cases, this function's time complexity degrades towards O(n*m). \n", - "
\n", - "
\n" - ], - "text/plain": [ - "zip :: [a] -> [b] -> [(a, b)]\n", - "URL: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:zip\n", - "zip takes two lists and returns a list of corresponding pairs. If one input list is short, excess elements of the longer list are discarded. \n", - "(>*<) :: Monoidal f => f a -> f b -> f (a, b)\n", - "URL: http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString-Builder-Prim.html#v:-62--42--60-\n", - "A pairing/concatenation operator for builder primitives, both bounded and fixed size.\n", + "(>*<) ∷ Monoidal f ⇒ f a → f b → f (a, b)
A pairing/concatenation operator for builder primitives, both bounded\n", + "and fixed size.\n", "\n", "For example,\n", "\n", - "> toLazyByteString (primFixed (char7 >*< char7) ('x','y')) = \"xy\"\n", + "
\n",
+       "toLazyByteString (primFixed (char7 >*< char7) ('x','y')) = \"xy\"\n",
+       "
\n", "\n", - "We can combine multiple primitives using >*< multiple times.\n", + "We can combine multiple primitives using >*< multiple\n", + "times.\n", "\n", - "> toLazyByteString (primFixed (char7 >*< char7 >*< char7) ('x',('y','z'))) = \"xyz\" \n", - "shrinkState :: ShrinkState s a => a -> s -> [(a, s)]\n", - "URL: http://hackage.haskell.org/packages/archive/QuickCheck/latest/doc/html/Test-QuickCheck-Modifiers.html#v:shrinkState\n", + "
\n",
+       "toLazyByteString (primFixed (char7 >*< char7 >*< char7) ('x',('y','z'))) = \"xyz\"\n",
+       "
\n", + "
\n", + "(>*<) ∷ Monoidal f ⇒ f a → f b → f (a, b)
Merge two functors into a tuple, analogous to liftA2\n", + "(,). (Sometimes known as **.)\n", + "
\n", + "zip ∷ List l ⇒ l a → l b → l (a, b)
\n", + "zip ∷ Zip f ⇒ f a → f b → f (a, b)
\n", + "zip ∷ (Zip f) ⇒ f a → f b → f (a, b)
\n", + "zip ∷ Zip f ⇒ f a → f b → f (a, b)
\n", + "zip ∷ Zip f ⇒ f a → f b → f (a, b)
\n", + "mzip ∷ MonadZip m ⇒ m a → m b → m (a, b)
\n", + "projectZip ∷ ProductIsoApplicative p ⇒ p a → p b → p (a, b)
Zipping projections.\n", + "
\n", + "(><) ∷ ProductIsoApplicative p ⇒ p a → p b → p (a, b)
Binary operator the same as projectZip.\n", + "
\n", + "projectZip ∷ ProductIsoApplicative p ⇒ p a → p b → p (a, b)
Zipping projections.\n", + "
\n", + "(><) ∷ ProductIsoApplicative p ⇒ p a → p b → p (a, b)
Binary operator the same as projectZip.\n", + "
\n", + "(><) ∷ ProductIsoApplicative p ⇒ p a → p b → p (a, b)
Binary operator the same as projectZip.\n", + "
\n", + "biunfold ∷ (Biunfoldable t, Unfolder f) ⇒ f a → f b → f (t a b)
Given a way to generate elements, return a way to generate structures\n", + "containing those elements.\n", + "
\n", + "biunfoldBF ∷ (Biunfoldable t, Unfolder f) ⇒ f a → f b → f (t a b)
Breadth-first unfold, which orders the result by the number of\n", + "choose calls.\n", + "
\n", + "deserializeWith2 ∷ (Serial2 f, MonadGet m) ⇒ m a → m b → m (f a b)
\n", + "biunfoldRestrict ∷ (BiunfoldableR predA predB t, predA a, predB b, Unfolder f) ⇒ f a → f b → f (t a b)
\n", + "biunfoldRestrictBF ∷ (BiunfoldableR p q t, Unfolder f, p a, q b) ⇒ f a → f b → f (t a b)
\n", + "mesh ∷ Graph g ⇒ [a] → [b] → g (a, b)
Construct a mesh graph from two lists of vertices. Complexity:\n", + "O(L1 * L2) time, memory and size, where L1 and L2\n", + "are the lengths of the given lists.\n", "\n", - "breakOnAll :: Text -> Text -> [(Text, Text)]\n", - "URL: http://hackage.haskell.org/packages/archive/text/latest/doc/html/Data-Text.html#v:breakOnAll\n", - "O(n+m) Find all non-overlapping instances of needle in haystack. Each element of the returned list consists of a pair:\n", + "
\n",
+       "mesh xs     []   == empty\n",
+       "mesh []     ys   == empty\n",
+       "mesh [x]    [y]  == vertex (x, y)\n",
+       "mesh xs     ys   == box (path xs) (path ys)\n",
+       "mesh [1..3] \"ab\" == edges [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a')), ((1,'b'),(2,'b')), ((2,'a'),(2,'b'))\n",
+       ", ((2,'a'),(3,'a')), ((2,'b'),(3,'b')), ((3,'a'),(3,'b')) ]\n",
+       "
\n", + "
\n", + "torus ∷ Graph g ⇒ [a] → [b] → g (a, b)
Construct a torus graph from two lists of vertices. Complexity:\n", + "O(L1 * L2) time, memory and size, where L1 and L2\n", + "are the lengths of the given lists.\n", "\n", - "* The entire string prior to the kth match (i.e. the prefix)\n", - "* The kth match, followed by the remainder of the string\n", + "
\n",
+       "torus xs    []   == empty\n",
+       "torus []    ys   == empty\n",
+       "torus [x]   [y]  == edge (x,y) (x,y)\n",
+       "torus xs    ys   == box (circuit xs) (circuit ys)\n",
+       "torus [1,2] \"ab\" == edges [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a')), ((1,'b'),(1,'a')), ((1,'b'),(2,'b'))\n",
+       ", ((2,'a'),(1,'a')), ((2,'a'),(2,'b')), ((2,'b'),(1,'b')), ((2,'b'),(2,'a')) ]\n",
+       "
\n", + "
\n", + "zipExactMay ∷ [a] → [b] → Maybe [(a, b)]
\n" + ], + "text/plain": [ + "zip :: [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/base/docs/Prelude.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", "\n", - "Examples:\n", + "
\n",
+       "zip [1, 2] ['a', 'b'] = [(1, 'a'), (2, 'b')]\n",
+       "
\n", "\n", - "> breakOnAll \"::\" \"\"\n", - "> ==> []\n", - "> breakOnAll \"/\" \"a/b/c/\"\n", - "> ==> [(\"a\", \"/b/c/\"), (\"a/b\", \"/c/\"), (\"a/b/c\", \"/\")]\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded:\n", "\n", - "In (unlikely) bad cases, this function's time complexity degrades towards O(n*m).\n", + "
\n",
+       "zip [1] ['a', 'b'] = [(1, 'a')]\n",
+       "zip [1, 2] ['a'] = [(1, 'a')]\n",
+       "
\n", "\n", - "The needle parameter may not be empty. \n", - "breakOnAll :: Text -> Text -> [(Text, Text)]\n", - "URL: http://hackage.haskell.org/packages/archive/text/latest/doc/html/Data-Text-Lazy.html#v:breakOnAll\n", - "O(n+m) Find all non-overlapping instances of needle in haystack. Each element of the returned list consists of a pair:\n", + "zip is right-lazy:\n", "\n", - "* The entire string prior to the kth match (i.e. the prefix)\n", - "* The kth match, followed by the remainder of the string\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "zip _|_ [] = _|_\n",
+       "
\n", "\n", - "Examples:\n", + "zip :: [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/base/docs/Data-List.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", "\n", - "> breakOnAll \"::\" \"\"\n", - "> ==> []\n", - "> breakOnAll \"/\" \"a/b/c/\"\n", - "> ==> [(\"a\", \"/b/c/\"), (\"a/b\", \"/c/\"), (\"a/b/c\", \"/\")]\n", + "
\n",
+       "zip [1, 2] ['a', 'b'] = [(1, 'a'), (2, 'b')]\n",
+       "
\n", "\n", - "This function is strict in its first argument, and lazy in its second.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded:\n", "\n", - "In (unlikely) bad cases, this function's time complexity degrades towards O(n*m).\n", + "
\n",
+       "zip [1] ['a', 'b'] = [(1, 'a')]\n",
+       "zip [1, 2] ['a'] = [(1, 'a')]\n",
+       "
\n", "\n", - "The needle parameter may not be empty. \n", - "genLNodes :: Enum a => a -> Int -> [LNode a]\n", - "URL: http://hackage.haskell.org/packages/archive/fgl/latest/doc/html/Data-Graph-Inductive-Example.html#v:genLNodes\n", - "generate list of labeled nodes \n", - "gmapAccumT :: Data d => (forall e. Data e => a -> e -> (a, e)) -> a -> d -> (a, d)\n", - "URL: http://hackage.haskell.org/packages/archive/syb/latest/doc/html/Data-Generics-Twins.html#v:gmapAccumT\n", - "gmapT with accumulation \n", - "threadList :: (Collect r c) -> (Split t i r) -> [i] -> t -> (c, t)\n", - "URL: http://hackage.haskell.org/packages/archive/fgl/latest/doc/html/Data-Graph-Inductive-Internal-Thread.html#v:threadList\n", + "zip is right-lazy:\n", "\n", - "threadList' :: (Collect r c) -> (Split t i r) -> [i] -> t -> (c, t)\n", - "URL: http://hackage.haskell.org/packages/archive/fgl/latest/doc/html/Data-Graph-Inductive-Internal-Thread.html#v:threadList-39-\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "zip _|_ [] = _|_\n",
+       "
\n", "\n", - "mapAccumL :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString)\n", - "URL: http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString-Lazy.html#v:mapAccumL\n", - "The mapAccumL function behaves like a combination of map and foldl; it applies a function to each element of a ByteString, passing an accumulating parameter from left to right, and returning a final value of this accumulator together with the new ByteString. \n", - "mapAccumL :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString)\n", - "URL: http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString.html#v:mapAccumL\n", - "The mapAccumL function behaves like a combination of map and foldl; it applies a function to each element of a ByteString, passing an accumulating parameter from left to right, and returning a final value of this accumulator together with the new list. \n", - "mapAccumR :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString)\n", - "URL: http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString.html#v:mapAccumR\n", - "The mapAccumR function behaves like a combination of map and foldr; it applies a function to each element of a ByteString, passing an accumulating parameter from right to left, and returning a final value of this accumulator together with the new ByteString. \n", - "mapAccumL :: (acc -> Char -> (acc, Char)) -> acc -> ByteString -> (acc, ByteString)\n", - "URL: http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString-Lazy-Char8.html#v:mapAccumL\n", - "The mapAccumL function behaves like a combination of map and foldl; it applies a function to each element of a ByteString, passing an accumulating parameter from left to right, and returning a final value of this accumulator together with the new ByteString. \n", - "mapAccumL :: (acc -> Char -> (acc, Char)) -> acc -> ByteString -> (acc, ByteString)\n", - "URL: http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString-Char8.html#v:mapAccumL\n", - "The mapAccumL function behaves like a combination of map and foldl; it applies a function to each element of a ByteString, passing an accumulating parameter from left to right, and returning a final value of this accumulator together with the new list. \n", - "mapAccumR :: (acc -> Char -> (acc, Char)) -> acc -> ByteString -> (acc, ByteString)\n", - "URL: http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString-Char8.html#v:mapAccumR\n", - "The mapAccumR function behaves like a combination of map and foldr; it applies a function to each element of a ByteString, passing an accumulating parameter from right to left, and returning a final value of this accumulator together with the new ByteString. \n", - "mapAccumL :: (a -> Char -> (a, Char)) -> a -> Text -> (a, Text)\n", - "URL: http://hackage.haskell.org/packages/archive/text/latest/doc/html/Data-Text.html#v:mapAccumL\n", - "O(n) Like a combination of map and foldl'. Applies a function to each element of a Text, passing an accumulating parameter from left to right, and returns a final Text. Performs replacement on invalid scalar values. \n", - "mapAccumR :: (a -> Char -> (a, Char)) -> a -> Text -> (a, Text)\n", - "URL: http://hackage.haskell.org/packages/archive/text/latest/doc/html/Data-Text.html#v:mapAccumR\n", - "The mapAccumR function behaves like a combination of map and a strict foldr; it applies a function to each element of a Text, passing an accumulating parameter from right to left, and returning a final value of this accumulator together with the new Text. Performs replacement on invalid scalar values. \n", - "execRWS :: RWS r w s a -> r -> s -> (s, w)\n", - "URL: http://hackage.haskell.org/packages/archive/transformers/latest/doc/html/Control-Monad-Trans-RWS-Lazy.html#v:execRWS\n", - "Evaluate a computation with the given initial state and environment, returning the final state and output, discarding the final value. \n", - "breakSubstring :: ByteString -> ByteString -> (ByteString, ByteString)\n", - "URL: http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString.html#v:breakSubstring\n", - "otherwise -> Just (length x) \n", + "zip :: [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/base/docs/GHC-List.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", "\n", - "For example, to tokenise a string, dropping delimiters:\n", + "
\n",
+       "zip [1, 2] ['a', 'b'] = [(1, 'a'), (2, 'b')]\n",
+       "
\n", "\n", - "> tokenise x y = h (:) if null t then [] else tokenise x (drop (length x) t)\n", - "> \n", + "If one input list is short, excess elements of the longer list are\n", + "discarded:\n", "\n", - "To skip to the first occurence of a string:\n", + "
\n",
+       "zip [1] ['a', 'b'] = [(1, 'a')]\n",
+       "zip [1, 2] ['a'] = [(1, 'a')]\n",
+       "
\n", "\n", - "> snd (breakSubstring x y)\n", + "zip is right-lazy:\n", "\n", - "To take the parts of a string before a delimiter:\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "zip _|_ [] = _|_\n",
+       "
\n", "\n", - "> fst (breakSubstring x y) \n", - "breakOn :: Text -> Text -> (Text, Text)\n", - "URL: http://hackage.haskell.org/packages/archive/text/latest/doc/html/Data-Text.html#v:breakOn\n", - "O(n+m) Find the first instance of needle (which must be non-null) in haystack. The first element of the returned tuple is the prefix of haystack before needle is matched. The second is the remainder of haystack, starting with the match.\n", + "zip :: [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/base/docs/GHC-OldList.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", "\n", - "Examples:\n", + "
\n",
+       "zip [1, 2] ['a', 'b'] = [(1, 'a'), (2, 'b')]\n",
+       "
\n", "\n", - "> breakOn \"::\" \"a::b::c\" ==> (\"a\", \"::b::c\")\n", - "> breakOn \"/\" \"foobar\" ==> (\"foobar\", \"\")\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded:\n", "\n", - "Laws:\n", + "
\n",
+       "zip [1] ['a', 'b'] = [(1, 'a')]\n",
+       "zip [1, 2] ['a'] = [(1, 'a')]\n",
+       "
\n", "\n", - "> append prefix match == haystack\n", - "> \n", + "zip is right-lazy:\n", "\n", - "If you need to break a string by a substring repeatedly (e.g. you want to break on every instance of a substring), use breakOnAll instead, as it has lower startup overhead.\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "zip _|_ [] = _|_\n",
+       "
\n", "\n", - "In (unlikely) bad cases, this function's time complexity degrades towards O(n*m)." + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/hspec/docs/Test-Hspec-Discover.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/Cabal/docs/Distribution-Compat-Prelude-Internal.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zipExact :: Partial => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/safe/docs/Safe-Exact.html#v:zipExact\n", + "
\n",
+       "zipExact xs ys =\n",
+       "| length xs == length ys = zip xs ys\n",
+       "| otherwise              = error \"some message\"\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/base-compat/docs/Prelude-Compat.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/protolude/docs/Protolude.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/numeric-prelude/docs/NumericPrelude.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/numeric-prelude/docs/NumericPrelude-Base.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/rio/docs/RIO.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/rio/docs/RIO-List.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/rebase/docs/Rebase-Prelude.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/fay-base/docs/Prelude.html#v:zip\n", + "\n", + "(+*+) :: [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/universe-base/docs/Data-Universe-Helpers.html#v:-43--42--43-\n", + "Slightly unfair 2-way Cartesian product: given two (possibly infinite)\n", + "lists, produce a single list such that whenever v and\n", + "w have finite indices in the input lists, (v,w) has\n", + "finite index in the output list. Lower indices occur as the\n", + "fst part of the tuple more frequently, but not exponentially\n", + "so.\n", + "\n", + "unfairCartesianProduct :: [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/universe-base/docs/Data-Universe-Helpers.html#v:unfairCartesianProduct\n", + "Very unfair 2-way Cartesian product: same guarantee as the slightly\n", + "unfair one, except that lower indices may occur as the fst\n", + "part of the tuple exponentially more frequently. This mainly exists as\n", + "a specification to test against.\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/llvm-hs-pure/docs/LLVM-Prelude.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/universum/docs/Universum-List-Reexport.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/haxl/docs/Haxl-Prelude.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/prelude-compat/docs/Data-List2010.html#v:zip\n", + "\n", + "zip :: [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/prelude-compat/docs/Prelude2010.html#v:zip\n", + "\n", + "zip :: [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/EdisonAPI/docs/Data-Edison-Seq-ListSeq.html#v:zip\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/LambdaHack/docs/Game-LambdaHack-Common-Prelude.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/LambdaHack/docs/Game-LambdaHack-Common-Prelude.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/intro/docs/Intro.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/yesod-paginator/docs/Yesod-Paginator-Prelude.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/hledger-web/docs/Hledger-Web-Import.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zip :: () => [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/relude/docs/Relude-List-Reexport.html#v:zip\n", + "zip takes two lists and returns a list of corresponding pairs.\n", + "If one input list is short, excess elements of the longer list are\n", + "discarded.\n", + "\n", + "zip is right-lazy:\n", + "\n", + "
\n",
+       "zip [] _|_ = []\n",
+       "
\n", + "\n", + "zipLazy :: [a] -> [b] -> [(a, b)]\n", + "URL: https://hackage.haskell.org/package/ghc/docs/Util.html#v:zipLazy\n", + "zipLazy is a kind of zip that is lazy in the second list\n", + "(observe the ~)\n", + "\n", + "concurrently :: MonadBaseControl IO m => m a -> m b -> m (a, b)\n", + "URL: https://hackage.haskell.org/package/lifted-async/docs/Control-Concurrent-Async-Lifted.html#v:concurrently\n", + "Generalized version of concurrently.\n", + "\n", + "concurrently :: forall m a b . (MonadBaseControl IO m, Forall (Pure m)) => m a -> m b -> m (a, b)\n", + "URL: https://hackage.haskell.org/package/lifted-async/docs/Control-Concurrent-Async-Lifted-Safe.html#v:concurrently\n", + "Generalized version of concurrently.\n", + "\n", + "pairADefault :: Applicative f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/invertible/docs/Control-Invertible-Monoidal.html#v:pairADefault\n", + "Default '>*< implementation for non-invertible\n", + "Applicatives.\n", + "\n", + "zip :: (Vector v a, Vector v b, Vector v (a, b)) => v a -> v b -> v (a, b)\n", + "URL: https://hackage.haskell.org/package/vector/docs/Data-Vector-Generic.html#v:zip\n", + "O(min(m,n)) Zip two vectors\n", + "\n", + "pair :: (Vector v a, Vector v b, Vector v (a, b)) => v a -> v b -> v (a, b)\n", + "URL: https://hackage.haskell.org/package/statistics/docs/Statistics-Sample.html#v:pair\n", + "Pair two samples. It's like zip but requires that both samples\n", + "have equal size.\n", + "\n", + "zip :: (Vector v a, Vector v b, Vector v (a, b)) => v a -> v b -> v (a, b)\n", + "URL: https://hackage.haskell.org/package/rio/docs/RIO-Vector.html#v:zip\n", + "O(min(m,n)) Zip two vectors\n", + "\n", + "concurrently :: MonadUnliftIO m => m a -> m b -> m (a, b)\n", + "URL: https://hackage.haskell.org/package/unliftio/docs/UnliftIO-Async.html#v:concurrently\n", + "Unlifted concurrently.\n", + "\n", + "concurrently :: MonadUnliftIO m => m a -> m b -> m (a, b)\n", + "URL: https://hackage.haskell.org/package/yesod-websockets/docs/Yesod-WebSockets.html#v:concurrently\n", + "Unlifted concurrently.\n", + "\n", + "zip :: Sequence s => s a -> s b -> s (a, b)\n", + "URL: https://hackage.haskell.org/package/EdisonAPI/docs/Data-Edison-Seq.html#v:zip\n", + "Combine two sequences into a sequence of pairs. If the sequences are\n", + "different lengths, the excess elements of the longer sequence is\n", + "discarded.\n", + "\n", + "
\n",
+       "zip <x0,...,xn-1> <y0,...,ym-1> = <(x0,y0),...,(xj-1,yj-1)>\n",
+       "where j = min {n,m}\n",
+       "
\n", + "\n", + "Axioms:\n", + "\n", + "
    \n", + "
  • zip xs ys = zipWith (,) xs ys
  • \n", + "
\n", + "\n", + "This function is always unambiguous.\n", + "\n", + "Default running time: O( min( n1, n2 ) )\n", + "\n", + "zipUsingLview :: Sequence s => s a -> s b -> s (a, b)\n", + "URL: https://hackage.haskell.org/package/EdisonCore/docs/Data-Edison-Seq-Defaults.html#v:zipUsingLview\n", + "\n", + "zipUsingLists :: Sequence s => s a -> s b -> s (a, b)\n", + "URL: https://hackage.haskell.org/package/EdisonCore/docs/Data-Edison-Seq-Defaults.html#v:zipUsingLists\n", + "\n", + "concurrently :: MonadConc m => m a -> m b -> m (a, b)\n", + "URL: https://hackage.haskell.org/package/concurrency/docs/Control-Concurrent-Classy-Async.html#v:concurrently\n", + "Run two MonadConc actions concurrently, and return both\n", + "results. If either action throws an exception at any time, then the\n", + "other action is cancelled, and the exception is re-thrown by\n", + "concurrently.\n", + "\n", + "
\n",
+       "concurrently left right =\n",
+       "withAsync left $ \\a ->\n",
+       "withAsync right $ \\b ->\n",
+       "waitBoth a b\n",
+       "
\n", + "\n", + "mzipRep :: Representable f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/adjunctions/docs/Data-Functor-Rep.html#v:mzipRep\n", + "\n", + "box :: Graph g => g a -> g b -> g (a, b)\n", + "URL: https://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-HigherKinded-Class.html#v:box\n", + "Compute the Cartesian product of graphs. Complexity: O(s1 *\n", + "s2) time, memory and size, where s1 and s2 are the\n", + "sizes of the given graphs.\n", + "\n", + "
\n",
+       "box (path [0,1]) (path \"ab\") == edges [ ((0,'a'), (0,'b'))\n",
+       ", ((0,'a'), (1,'a'))\n",
+       ", ((0,'b'), (1,'b'))\n",
+       ", ((1,'a'), (1,'b')) ]\n",
+       "
\n", + "\n", + "Up to an isomorphism between the resulting vertex types, this\n", + "operation is commutative, associative,\n", + "distributes over overlay, has singleton graphs as\n", + "identities and empty as the annihilating zero.\n", + "Below ~~ stands for the equality up to an isomorphism, e.g.\n", + "(x, ()) ~~ x.\n", + "\n", + "
\n",
+       "box x y               ~~ box y x\n",
+       "box x (box y z)       ~~ box (box x y) z\n",
+       "box x (overlay y z)   == overlay (box x y) (box x z)\n",
+       "box x (vertex ())     ~~ x\n",
+       "box x empty           ~~ empty\n",
+       "vertexCount (box x y) == vertexCount x * vertexCount y\n",
+       "edgeCount   (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y\n",
+       "
\n", + "\n", + "divided :: Divisible f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/contravariant/docs/Data-Functor-Contravariant-Divisible.html#v:divided\n", + "
\n",
+       "divided = divide id\n",
+       "
\n", + "\n", + "(>*<) :: Divisible f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/dhall/docs/Dhall.html#v:-62--42--60-\n", + "The RecordInputType divisible (contravariant) functor allows\n", + "you to build an InputType injector for a Dhall record.\n", + "\n", + "For example, let's take the following Haskell data type:\n", + "\n", + "
\n",
+       "data Project = Project\n",
+       "{ projectName :: Text\n",
+       ", projectDescription :: Text\n",
+       ", projectStars :: Natural\n",
+       "}\n",
+       "
\n", + "\n", + "And assume that we have the following Dhall record that we would like\n", + "to parse as a Project:\n", + "\n", + "
\n",
+       "{ name =\n",
+       "\"dhall-haskell\"\n",
+       ", description =\n",
+       "\"A configuration language guaranteed to terminate\"\n",
+       ", stars =\n",
+       "289\n",
+       "}\n",
+       "
\n", + "\n", + "Our injector has type InputType Project, but we can't\n", + "build that out of any smaller injectors, as InputTypes cannot\n", + "be combined (they are only Contravariants). However, we can use\n", + "an InputRecordType to build an InputType for\n", + "Project:\n", + "\n", + "
\n",
+       "injectProject :: InputType Project\n",
+       "injectProject =\n",
+       "inputRecord\n",
+       "(  adapt >$< inputFieldWith \"name\" inject\n",
+       ">*< inputFieldWith \"description\" inject\n",
+       ">*< inputFieldWith \"stars\" inject\n",
+       ")\n",
+       "where\n",
+       "adapt (Project{..}) = (projectName, (projectDescription, projectStars))\n",
+       "
\n", + "\n", + "Or, since we are simply using the Inject instance to inject\n", + "each field, we could write\n", + "\n", + "
\n",
+       "injectProject :: InputType Project\n",
+       "injectProject =\n",
+       "inputRecord\n",
+       "(  adapt >$< inputField \"name\"\n",
+       ">*< inputField \"description\"\n",
+       ">*< inputField \"stars\"\n",
+       ")\n",
+       "where\n",
+       "adapt (Project{..}) = (projectName, (projectDescription, projectStars))\n",
+       "
\n", + "\n", + "Infix divided\n", + "\n", + "divided :: Divisible f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/rebase/docs/Rebase-Prelude.html#v:divided\n", + "
\n",
+       "divided = divide id\n",
+       "
\n", + "\n", + "(>*<) :: Divisible f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/rebase/docs/Rebase-Prelude.html#v:-62--42--60-\n", + "An alias to divided.\n", + "\n", + "(>*<) :: Divisible f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/contravariant-extras/docs/Contravariant-Extras.html#v:-62--42--60-\n", + "An alias to divided.\n", + "\n", + "contrazip2 :: Divisible f => f a1 -> f a2 -> f (a1, a2)\n", + "URL: https://hackage.haskell.org/package/rebase/docs/Rebase-Prelude.html#v:contrazip2\n", + "\n", + "contrazip2 :: forall f a1 a2 . Divisible f => f a1 -> f a2 -> f (a1, a2)\n", + "URL: https://hackage.haskell.org/package/contravariant-extras/docs/Contravariant-Extras-Contrazip.html#v:contrazip2\n", + "\n", + "pair :: Sized f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/size-based/docs/Control-Sized.html#v:pair\n", + "Default: pair a b = (,) $ a * b.\n", + "\n", + "(>*<) :: Monoidal f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/bytestring/docs/Data-ByteString-Builder-Prim.html#v:-62--42--60-\n", + "A pairing/concatenation operator for builder primitives, both bounded\n", + "and fixed size.\n", + "\n", + "For example,\n", + "\n", + "
\n",
+       "toLazyByteString (primFixed (char7 >*< char7) ('x','y')) = \"xy\"\n",
+       "
\n", + "\n", + "We can combine multiple primitives using >*< multiple\n", + "times.\n", + "\n", + "
\n",
+       "toLazyByteString (primFixed (char7 >*< char7 >*< char7) ('x',('y','z'))) = \"xyz\"\n",
+       "
\n", + "\n", + "(>*<) :: Monoidal f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/invertible/docs/Control-Invertible-Monoidal.html#v:-62--42--60-\n", + "Merge two functors into a tuple, analogous to liftA2\n", + "(,). (Sometimes known as **.)\n", + "\n", + "zip :: List l => l a -> l b -> l (a, b)\n", + "URL: https://hackage.haskell.org/package/List/docs/Data-List-Class.html#v:zip\n", + "\n", + "zip :: Zip f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/classy-prelude/docs/ClassyPrelude.html#v:zip\n", + "\n", + "zip :: (Zip f) => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/non-empty/docs/Data-NonEmpty-Class.html#v:zip\n", + "\n", + "zip :: Zip f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/keys/docs/Data-Key.html#v:zip\n", + "\n", + "zip :: Zip f => f a -> f b -> f (a, b)\n", + "URL: https://hackage.haskell.org/package/chunked-data/docs/Data-ChunkedZip.html#v:zip\n", + "\n", + "mzip :: MonadZip m => m a -> m b -> m (a, b)\n", + "URL: https://hackage.haskell.org/package/base/docs/Control-Monad-Zip.html#v:mzip\n", + "\n", + "projectZip :: ProductIsoApplicative p => p a -> p b -> p (a, b)\n", + "URL: https://hackage.haskell.org/package/relational-query/docs/Database-Relational-Arrow.html#v:projectZip\n", + "Zipping projections.\n", + "\n", + "(><) :: ProductIsoApplicative p => p a -> p b -> p (a, b)\n", + "URL: https://hackage.haskell.org/package/relational-query/docs/Database-Relational-Arrow.html#v:-62--60-\n", + "Binary operator the same as projectZip.\n", + "\n", + "projectZip :: ProductIsoApplicative p => p a -> p b -> p (a, b)\n", + "URL: https://hackage.haskell.org/package/relational-query/docs/Database-Relational-Projectable.html#v:projectZip\n", + "Zipping projections.\n", + "\n", + "(><) :: ProductIsoApplicative p => p a -> p b -> p (a, b)\n", + "URL: https://hackage.haskell.org/package/relational-query/docs/Database-Relational-Projectable.html#v:-62--60-\n", + "Binary operator the same as projectZip.\n", + "\n", + "(><) :: ProductIsoApplicative p => p a -> p b -> p (a, b)\n", + "URL: https://hackage.haskell.org/package/relational-record/docs/Database-Relational-Documentation.html#v:-62--60-\n", + "Binary operator the same as projectZip.\n", + "\n", + "biunfold :: (Biunfoldable t, Unfolder f) => f a -> f b -> f (t a b)\n", + "URL: https://hackage.haskell.org/package/unfoldable/docs/Data-Biunfoldable.html#v:biunfold\n", + "Given a way to generate elements, return a way to generate structures\n", + "containing those elements.\n", + "\n", + "biunfoldBF :: (Biunfoldable t, Unfolder f) => f a -> f b -> f (t a b)\n", + "URL: https://hackage.haskell.org/package/unfoldable/docs/Data-Biunfoldable.html#v:biunfoldBF\n", + "Breadth-first unfold, which orders the result by the number of\n", + "choose calls.\n", + "\n", + "deserializeWith2 :: (Serial2 f, MonadGet m) => m a -> m b -> m (f a b)\n", + "URL: https://hackage.haskell.org/package/bytes/docs/Data-Bytes-Serial.html#v:deserializeWith2\n", + "\n", + "biunfoldRestrict :: (BiunfoldableR predA predB t, predA a, predB b, Unfolder f) => f a -> f b -> f (t a b)\n", + "URL: https://hackage.haskell.org/package/unfoldable-restricted/docs/Data-Unfoldable-Restricted.html#v:biunfoldRestrict\n", + "\n", + "biunfoldRestrictBF :: (BiunfoldableR p q t, Unfolder f, p a, q b) => f a -> f b -> f (t a b)\n", + "URL: https://hackage.haskell.org/package/unfoldable-restricted/docs/Data-Unfoldable-Restricted.html#v:biunfoldRestrictBF\n", + "\n", + "mesh :: Graph g => [a] -> [b] -> g (a, b)\n", + "URL: https://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-HigherKinded-Class.html#v:mesh\n", + "Construct a mesh graph from two lists of vertices. Complexity:\n", + "O(L1 * L2) time, memory and size, where L1 and L2\n", + "are the lengths of the given lists.\n", + "\n", + "
\n",
+       "mesh xs     []   == empty\n",
+       "mesh []     ys   == empty\n",
+       "mesh [x]    [y]  == vertex (x, y)\n",
+       "mesh xs     ys   == box (path xs) (path ys)\n",
+       "mesh [1..3] \"ab\" == edges [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a')), ((1,'b'),(2,'b')), ((2,'a'),(2,'b'))\n",
+       ", ((2,'a'),(3,'a')), ((2,'b'),(3,'b')), ((3,'a'),(3,'b')) ]\n",
+       "
\n", + "\n", + "torus :: Graph g => [a] -> [b] -> g (a, b)\n", + "URL: https://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-HigherKinded-Class.html#v:torus\n", + "Construct a torus graph from two lists of vertices. Complexity:\n", + "O(L1 * L2) time, memory and size, where L1 and L2\n", + "are the lengths of the given lists.\n", + "\n", + "
\n",
+       "torus xs    []   == empty\n",
+       "torus []    ys   == empty\n",
+       "torus [x]   [y]  == edge (x,y) (x,y)\n",
+       "torus xs    ys   == box (circuit xs) (circuit ys)\n",
+       "torus [1,2] \"ab\" == edges [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a')), ((1,'b'),(1,'a')), ((1,'b'),(2,'b'))\n",
+       ", ((2,'a'),(1,'a')), ((2,'a'),(2,'b')), ((2,'b'),(1,'b')), ((2,'b'),(2,'a')) ]\n",
+       "
\n", + "\n", + "zipExactMay :: [a] -> [b] -> Maybe [(a, b)]\n", + "URL: https://hackage.haskell.org/package/safe/docs/Safe-Exact.html#v:zipExactMay" ] }, "metadata": {}, @@ -2236,10 +3202,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "If you need a refresher on all of the options, you can just use `:help`:" ] @@ -2247,11 +3210,7 @@ { "cell_type": "code", "execution_count": 26, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -2288,10 +3247,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "All of the code you normally put into IHaskell is (like in GHCi) interpreted. However, sometimes you've perfected a function, and now need it to run faster. In that case, you can go ahead and define a module in a single cell. As long as your module has a module header along the lines of `module Name where`, IHaskell will recognize it as a module. It will create the file `A/B.hs`, compile it, and load it. " ] @@ -2299,11 +3255,7 @@ { "cell_type": "code", "execution_count": 27, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "-- If your code isn't running fast enough, you can just put it into a module.\n", @@ -2316,10 +3268,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "Note that the module is by default imported unqualified, as though you had typed `import A.B`." ] @@ -2327,11 +3276,7 @@ { "cell_type": "code", "execution_count": 28, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -2360,10 +3305,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "Note that since a new module is imported, all previous bound identifiers are now unbound. For instance, we no longer have access to the `f` function from before:" ] @@ -2371,11 +3313,7 @@ { "cell_type": "code", "execution_count": 29, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -2477,10 +3415,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "However, if you re-import this module with another import statement, the original implicit import goes away." ] @@ -2488,11 +3423,7 @@ { "cell_type": "code", "execution_count": 30, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [ { "data": { @@ -2522,10 +3453,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "Thanks!\n", "---\n", @@ -2549,7 +3477,8 @@ "codemirror_mode": "ihaskell", "file_extension": ".hs", "name": "haskell", - "version": "8.0.2" + "pygments_lexer": "Haskell", + "version": "8.4.3" }, "latex_envs": { "bibliofile": "biblio.bib", @@ -2570,5 +3499,5 @@ } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } diff --git a/src/IHaskell/Eval/Hoogle.hs b/src/IHaskell/Eval/Hoogle.hs index bb326999..b946b324 100644 --- a/src/IHaskell/Eval/Hoogle.hs +++ b/src/IHaskell/Eval/Hoogle.hs @@ -1,24 +1,31 @@ -{-# LANGUAGE NoImplicitPrelude, FlexibleInstances, OverloadedStrings #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} module IHaskell.Eval.Hoogle ( search, document, render, OutputFormat(..), - HoogleResult, + HoogleResult(..), + HoogleResponse(..), + parseResponse, ) where +import qualified Data.ByteString.Char8 as CBS +import qualified Data.ByteString.Lazy as LBS +import Data.Either (either) import IHaskellPrelude -import qualified Data.ByteString.Lazy as LBS -import qualified Data.ByteString.Char8 as CBS +import Data.Aeson +import Data.Char (isAlphaNum, isAscii) +import qualified Data.List as List +import qualified Data.Text as T +import Data.Vector (toList) import Network.HTTP.Client import Network.HTTP.Client.TLS -import Data.Aeson -import qualified Data.List as List -import Data.Char (isAscii, isAlphaNum) -import StringUtils (split, strip, replace) +import StringUtils (replace, split, strip) -- | Types of formats to render output to. data OutputFormat = Plain -- ^ Render to plain text. @@ -35,17 +42,19 @@ data HoogleResult = SearchResult HoogleResponse data HoogleResponseList = HoogleResponseList [HoogleResponse] instance FromJSON HoogleResponseList where - parseJSON (Object obj) = do - results <- obj .: "results" - HoogleResponseList <$> mapM parseJSON results + parseJSON (Array arr) = + HoogleResponseList <$> mapM parseJSON (toList arr) - parseJSON _ = fail "Expected object with 'results' field." + parseJSON _ = fail "Expected array." instance FromJSON HoogleResponse where parseJSON (Object obj) = - HoogleResponse <$> obj .: "location" <*> obj .: "self" <*> obj .: "docs" + HoogleResponse + <$> obj .: "url" + <*> (removeMarkup <$> obj .: "item") + <*> obj .: "docs" - parseJSON _ = fail "Expected object with fields: location, self, docs" + parseJSON _ = fail "Expected object with fields: url, item, docs" -- | Query Hoogle for the given string. This searches Hoogle using the internet. It returns either -- an error message or the successful JSON result. @@ -59,7 +68,7 @@ query str = do where queryUrl :: String -> String - queryUrl = printf "https://www.haskell.org/hoogle/?hoogle=%s&mode=json" + queryUrl = printf "http://hoogle.haskell.org/?hoogle=%s&mode=json" -- | Copied from the HTTP package. urlEncode :: String -> String @@ -87,18 +96,16 @@ urlEncode (ch:t) -- | Search for a query on Hoogle. Return all search results. search :: String -> IO [HoogleResult] -search string = do - response <- query string - return $ - case response of - Left err -> [NoResult err] - Right jsn -> - case eitherDecode $ LBS.fromStrict $ CBS.pack jsn of - Left err -> [NoResult err] - Right results -> - case map SearchResult $ (\(HoogleResponseList l) -> l) results of - [] -> [NoResult "no matching identifiers found."] - res -> res +search string = either ((:[]) . NoResult) parseResponse <$> query string + +parseResponse :: String -> [HoogleResult] +parseResponse jsn = + case eitherDecode $ LBS.fromStrict $ CBS.pack jsn of + Left err -> [NoResult err] + Right results -> + case map SearchResult $ (\(HoogleResponseList l) -> l) results of + [] -> [NoResult "no matching identifiers found."] + res -> res -- | Look up an identifier on Hoogle. Return documentation for that identifier. If there are many -- identifiers, include documentation for all of them. @@ -118,13 +125,13 @@ document string = do matches _ = False toDocResult (SearchResult resp) = Just $ DocResult resp - toDocResult (DocResult _) = Nothing - toDocResult (NoResult _) = Nothing + toDocResult (DocResult _) = Nothing + toDocResult (NoResult _) = Nothing -- | Render a Hoogle search result into an output format. render :: OutputFormat -> HoogleResult -> String render Plain = renderPlain -render HTML = renderHtml +render HTML = renderHtml -- | Render a Hoogle result to plain text. renderPlain :: HoogleResult -> String @@ -181,6 +188,12 @@ renderSelf string loc span "hoogle-class" (link loc $ extractNewtype string) ++ packageSub package + | "type" `isPrefixOf` string = + let package = extractPackageName loc + in nwt ++ " " ++ + span "hoogle-class" (link loc $ extractType string) ++ + packageSub package + | otherwise = let [name, args] = split "::" string package = extractPackageName loc @@ -197,6 +210,7 @@ renderSelf string loc extractClass = strip . replace "class" "" extractData = strip . replace "data" "" extractNewtype = strip . replace "newtype" "" + extractType = strip . replace "newtype" "" pkg = span "hoogle-head" "package" mdl = span "hoogle-head" "module" cls = span "hoogle-head" "class" @@ -223,21 +237,7 @@ renderSelf string loc ", " ++ mdl ++ " " ++ span "hoogle-module" modname ++ ")" renderDocs :: String -> String -renderDocs doc = - let groups = List.groupBy bothAreCode $ lines doc - nonull = filter (not . null . strip) - bothAreCode s1 s2 = - isPrefixOf ">" (strip s1) && - isPrefixOf ">" (strip s2) - isCode xs = - case xs of - [] -> False - (s:_) -> isPrefixOf ">" $ strip s - makeBlock xs = - if isCode xs - then div' "hoogle-code" $ unlines $ nonull xs - else div' "hoogle-text" $ unlines $ nonull xs - in div' "hoogle-doc" $ unlines $ map makeBlock groups +renderDocs doc = div' "hoogle-doc" doc extractPackageName :: String -> Maybe String extractPackageName lnk = do @@ -259,3 +259,19 @@ span = printf "%s" link :: String -> String -> String link = printf "%s" + +-- | very explicit cleaning of the type signature in the hoogle 5 response, +-- to remove html markup and escaped characters. +removeMarkup :: String -> String +removeMarkup s = T.unpack $ List.foldl (flip ($)) (T.pack s) replaceAll + where replacements :: [ (T.Text, T.Text) ] + replacements = [ ( "", "" ) + , ( "", "" ) + , ( "<0>", "" ) + , ( "", "" ) + , ( ">", ">" ) + , ( "<", "<" ) + , ( "", "") + , ( "", "") + ] + replaceAll = uncurry T.replace <$> replacements diff --git a/src/tests/Hspec.hs b/src/tests/Hspec.hs index 38b58169..eb708f3b 100644 --- a/src/tests/Hspec.hs +++ b/src/tests/Hspec.hs @@ -7,10 +7,12 @@ import Test.Hspec import IHaskell.Test.Completion (testCompletions) import IHaskell.Test.Parser (testParser) import IHaskell.Test.Eval (testEval) +import IHaskell.Test.Hoogle (testHoogle) main :: IO () -main = +main = hspec $ do testParser testEval testCompletions + testHoogle diff --git a/src/tests/IHaskell/Test/Hoogle.hs b/src/tests/IHaskell/Test/Hoogle.hs new file mode 100644 index 00000000..49227b52 --- /dev/null +++ b/src/tests/IHaskell/Test/Hoogle.hs @@ -0,0 +1,63 @@ +{-# LANGUAGE QuasiQuotes #-} + +module IHaskell.Test.Hoogle ( testHoogle ) where + +import Test.Hspec +import Text.RawString.QQ + +import IHaskell.Eval.Hoogle +-- import Data.Text (unpack) +-- import qualified Data.Text.IO as T +preludeFmapJson :: String +preludeFmapJson = [r| +[ + { + "url": "https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap", + "module": { + "url": "https://hackage.haskell.org/package/base/docs/Prelude.html", + "name": "Prelude" + }, + "package": { + "url": "https://hackage.haskell.org/package/base", + "name": "base" + }, + "item": "<0>fmap :: Functor f => (a -> b) -> f a -> f b", + "type": "", + "docs": "" + } +]|] + +moduleJson :: String +moduleJson = [r| +[ + { + "url": "https://hackage.haskell.org/package/universum/docs/Universum-Functor-Fmap.html", + "module": {}, + "package": { + "url": "https://hackage.haskell.org/package/universum", + "name": "universum" + }, + "item": "module Universum.Functor.<0>Fmap", + "type": "module", + "docs": "This module contains useful functions to work with Functor type\nclass.\n" + } +]|] + +testHoogle :: Spec +testHoogle = describe "Hoogle Search" $ do + describe "fmap search result" $ do + let results = parseResponse preludeFmapJson :: [HoogleResult] + it "should find 1 results" $ do + length results `shouldBe` 1 + let (SearchResult (HoogleResponse loc signature _docUrl)) = head results + it "should not contain html markup" $ do + loc `shouldBe` "https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap" + signature `shouldBe` "fmap :: Functor f => (a -> b) -> f a -> f b" + describe "module result" $ do + let results = parseResponse moduleJson :: [HoogleResult] + let (SearchResult (HoogleResponse _loc signature _docUrl)) = head results + it "should not contain html markup" $ do + signature `shouldBe` "module Universum.Functor.Fmap" + it "should be renderable" $ do + (render Plain $ head results) `shouldStartWith` "module Universum.Functor.Fmap" + (render HTML $ head results) `shouldStartWith` "module"