{"id":13582706,"url":"https://github.com/chrisdone-archive/jl","last_synced_at":"2025-10-21T13:41:12.264Z","repository":{"id":22797579,"uuid":"97342210","full_name":"chrisdone-archive/jl","owner":"chrisdone-archive","description":"Functional sed for JSON","archived":true,"fork":false,"pushed_at":"2022-05-17T07:42:09.000Z","size":95,"stargazers_count":473,"open_issues_count":5,"forks_count":19,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-03T22:47:34.240Z","etag":null,"topics":["command-line","command-line-tool","haskell","json"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chrisdone-archive.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"patreon":"chrisdone"}},"created_at":"2017-07-15T21:03:09.000Z","updated_at":"2024-11-16T00:06:50.000Z","dependencies_parsed_at":"2022-07-24T18:02:34.561Z","dependency_job_id":null,"html_url":"https://github.com/chrisdone-archive/jl","commit_stats":null,"previous_names":["chrisdone-archive/jl","chrisdone/jl"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisdone-archive%2Fjl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisdone-archive%2Fjl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisdone-archive%2Fjl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisdone-archive%2Fjl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrisdone-archive","download_url":"https://codeload.github.com/chrisdone-archive/jl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247495832,"owners_count":20948115,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["command-line","command-line-tool","haskell","json"],"created_at":"2024-08-01T15:02:57.323Z","updated_at":"2025-10-21T13:41:11.903Z","avatar_url":"https://github.com/chrisdone-archive.png","language":"Haskell","funding_links":["https://patreon.com/chrisdone"],"categories":["Haskell"],"sub_categories":[],"readme":"# jl [![Build Status](https://travis-ci.org/chrisdone/jl.svg)](https://travis-ci.org/chrisdone/jl)\njl (\"JSON lambda\") is a tiny functional language for querying and\nmanipulating JSON.\n\nExample:\n\n``` haskell\n$ jl 'map $ \\o -\u003e { sha: o.sha, ps: map _.sha o.parents }' x.json\n[{\"sha\":\"7b81a836c31500e685d043729259affa8b670a87\",\"ps\":[\"c538237f4e4c381d35f1c15497c...\n```\n\n## Installing\n\nBinary releases for Linux and OS X are available [here](https://github.com/chrisdone/jl/releases).\n\nBuilds on Windows (see [AppVeyor status](https://ci.appveyor.com/project/chrisdone/jl)), haven't added Windows\nbinaries to the releases yet.\n\nInstalling from source:\n\n1. Get [stack](https://haskell-lang.org/get-started)\n2. Run `stack install` in the repository directory.\n3. Add `~/.local/bin/` to your `PATH`.\n\n## Core syntax\n\nLiterals:\n\n    123, 4.5, -6, \"hi\", null, true, false\n\nLambdas:\n\n    \\x -\u003e y\n\nFunction application\n\n    get \"f\" o\n\nArithmetic:\n\n    x * (4 + 3)\n\nObjects:\n\n    {foo: 123, bar: 34.3, \"a:b\": \"hi\"}\n\nArrays:\n\n    [1, 4 * 5, id 5]\n\nConditionals:\n\n    if x then y else z\n\nShort-hand for fields:\n\n    o.f  is sugar for         get \"f\" o\n    _.f  is sugar for  (\\o -\u003e get \"f\" o)\n\nFor arrays:\n\n    _[0] is sugar for   (\\o -\u003e get 0 o)\n\nOr objects:\n\n    _[k]     is sugar for   (\\o -\u003e get k o)\n    _[\"foo\"] is sugar for   (\\o -\u003e get \"foo\" o)\n\nFunction composition:\n\n    a | b | c is sugar for `\\x -\u003e c (b (a x))`\n\n## Mini tutorial\n\nYou do everything with usual functional programming functions.\n\nReturning the same thing, aka identity. That's normal in functional\nprogramming:\n\n``` haskell\njl 'id'\n```\n\nA sequence of JSON strings will be read in and processed individually:\n\nE.g.\n\n``` haskell\n$ cat x.json | jl id\n{\"a\":1}\n{\"a\":2}\n{\"a\":3}\n{\"a\":4}\n```\n\nIf you want to read the input in as an array, use `--array`:\n\n``` haskell\n$ cat x.json | jl --array 'map _.a'\n[1,2,3,4]\n```\n\nAfter processing, sometimes you want to print each element of the\narray out line by line, for that use `--lines`:\n\n``` haskell\n$ cat x.json | jl --array --lines 'map _.a'\n1\n2\n3\n4\n```\n\nTaking the first element of something, using syntax that looks like\nregular array access. The `_` is a short-hand so that you don't need a\nlambda:\n\n``` haskell\njl '_[0]'\n```\n\nIf you want to get what keys are available, you can run:\n\n``` haskell\njl 'map keys | _[0]'\n[\"sha\",\"committer\",\"url\",\"comments_url\",\"parents\",\"author\",\"html_url\",\"commit\"]\n```\n\nTaking the first element and then creating a record of some parts of it:\n\n``` haskell\njl '_[0] | \\o -\u003e {msg: o.commit.message, n: o.commit.committer.name}'\n```\n\nNote the use of `|` to compose functions. Just like in the shell.\n\nApplying a function to all elements in an array:\n\n``` haskell\njl 'map _.commit.committer.name'\n```\n\nNote how you can nest property access easily.\n\nApplying something more detailed, by constructing a record of our own\n\n``` haskell\njl 'map $ \\o -\u003e {msg: o.commit.message, n: o.commit.committer.name}'\n```\n\nYou can use `$` to avoid using parentheses on the right. That's a\ntrick from Haskell.\n\nApplying functions to nested data structures:\n\n``` haskell\njl '_[0] | \\o -\u003e {msg: o.commit.message, n: o.commit.committer.name, ps: map _.html_url o.parents }'\n```\n\nNotice the `ps` property comes by taking the `html_url` of all the parents.\n\nFiltering is easy, simply write a function that returns true:\n\n``` haskell\njl 'map (\\o -\u003e { sha: o.sha, ps: map _.sha o.parents }) | filter (\\o -\u003e length o.ps \u003e 1)'\n```\n\nIf you want to make an object with arbitrary keys that come at runtime, use `set`:\n\n``` haskell\n$ echo '\"hello\"' | jl '\\x -\u003e set x 123 {}'\n{\"hello\":123}\n```\n\nThis sets the key `x` in the empty object `{}` to `\"hello\"` with the value `123`.\nYou can use `set` repeatedly to construct more keys.\n\nIf you want to construct an object from a list of key/values, you can use `fold`:\n\n```haskell\n$ echo '[{\"k\":\"foo\",\"v\":123},{\"k\":\"bar\",\"v\":456}]' | jl 'fold (\\acc o -\u003e set o.k o.v acc) {}'\n{\"foo\":123,\"bar\":456}\n```\n\n# Available functions\n\n## Record access\n\n```haskell\nget :: JSON → JSON → JSON\n```\n\nGet the value at k from the object\n\n```haskell\nset :: JSON → JSON → JSON → JSON\n```\n\nSet the value k to v in object\n\n```haskell\nmodify :: JSON → (JSON → JSON) → JSON → JSON\n```\n\nModify the object at k with function f\n\n```haskell\nkeys :: JSON → JSON\n```\n\nGet all keys of the object\n\n```haskell\nelems :: JSON → JSON\n```\n\nGet all elements of the object\n\n\n## Sequences\n\n```haskell\nmap :: (JSON → JSON) → JSON → JSON\n```\n\nApply a function to every element in the sequence\n\n```haskell\nfilter :: (JSON → JSON) → JSON → JSON\n```\n\nKeep only items from the sequence for which p returns true\n\n```haskell\ntakeWhile :: (JSON → JSON) → JSON → JSON\n```\n\nTake elements from a sequence while given predicate is true\n\n```haskell\nempty :: JSON → JSON\n```\n\nIs a sequence empty?\n\n```haskell\nlength :: JSON → JSON\n```\n\nGet the length of a sequence\n\n```haskell\nreverse :: JSON → JSON\n```\n\nReverse a sequence\n\n```haskell\ndrop :: JSON → JSON → JSON\n```\n\nDrop n items from the sequence\n\n```haskell\nelem :: JSON → JSON → JSON\n```\n\nIs x an element of y?\n\n```haskell\nconcat :: JSON → JSON\n```\n\nConcatenate a list of sequences into one sequence\n\n```haskell\nzipWith :: (JSON → JSON → JSON) → JSON → JSON → JSON\n```\n\nZip two lists calling with each element to f x y\n\n```haskell\ntake :: JSON → JSON → JSON\n```\n\nTake n items from sequence\n\n```haskell\nfold :: (JSON → JSON → JSON) → JSON → JSON → JSON\n```\n\nFold over a structure with a state.\n\n```haskell\ndropWhile :: (JSON → JSON) → JSON → JSON\n```\n\nDrop elements from a sequence while a predicate is true\n\n```haskell\nany :: (JSON → JSON) → JSON → JSON\n```\n\nDoes p return true for any of the elements?\n\n```haskell\nall :: (JSON → JSON) → JSON → JSON\n```\n\nDoes p return true for all of the elements?\n\n```haskell\nnub :: JSON → JSON\n```\n\nReturn the sequence with no duplicates; the nub of it\n\n```haskell\nsort :: JSON → JSON\n```\n\nReturn the sequence sorted\n\n```haskell\nappend :: JSON → JSON → JSON\n```\n\nAppend the members of the second sequence to the first sequence\n\n```haskell\nsum :: JSON → JSON\n```\n\nGet the sum of a sequence\n\n```haskell\nproduct :: JSON → JSON\n```\n\nGet the product of a sequence\n\n```haskell\nminimum :: JSON → JSON\n```\n\nGet the minimum of a sequence\n\n```haskell\nmaximum :: JSON → JSON\n```\n\nGet the maximum of a sequence\n\n\n## Strings\n\n```haskell\nwords :: JSON → JSON\n```\n\nSplit the string into a list of words\n\n```haskell\nunwords :: JSON → JSON\n```\n\nJoin the list of strings into a string separated by spaces\n\n```haskell\nlines :: JSON → JSON\n```\n\nSplit the string into a list of lines\n\n```haskell\nunlines :: JSON → JSON\n```\n\nJoin the list of strings into a string separated by lines and terminated by a new line\n\n\n## Predicate operators\n\n```haskell\n/= :: JSON → JSON → JSON\n```\n\na /= b\n\n```haskell\n= :: JSON → JSON → JSON\n```\n\na = b\n\n\n## Boolean operators\n\n```haskell\n\u0026\u0026 :: JSON → JSON → JSON\n```\n\na \u0026\u0026 b\n\n```haskell\n|| :: JSON → JSON → JSON\n```\n\na || b\n\n```haskell\nnot :: JSON → JSON\n```\n\nnot b\n\n\n## Numeric operators\n\n```haskell\n\u003e :: JSON → JSON → JSON\n```\n\na \u003e b\n\n```haskell\n\u003c :: JSON → JSON → JSON\n```\n\na \u003c b\n\n```haskell\n\u003e= :: JSON → JSON → JSON\n```\n\na \u003e= b\n\n```haskell\n\u003c= :: JSON → JSON → JSON\n```\n\na \u003c= b\n\n```haskell\n* :: JSON → JSON → JSON\n```\n\na * b\n\n```haskell\n+ :: JSON → JSON → JSON\n```\n\na + b\n\n```haskell\n- :: JSON → JSON → JSON\n```\n\na - b\n\n```haskell\n/ :: JSON → JSON → JSON\n```\n\na / b\n\n```haskell\nmin :: JSON → JSON → JSON\n```\n\na min b\n\n```haskell\nmax :: JSON → JSON → JSON\n```\n\na max b\n\n```haskell\nabs :: JSON → JSON\n```\n\nabs b\n\n\n## Function combinators\n\n```haskell\nid :: JSON → JSON\n```\n\nIdentity function, returns its input unchanged\n\n```haskell\ncompose :: (JSON → JSON) → (JSON → JSON) → JSON → JSON\n```\n\nCompose two functions\n\n```haskell\nflip :: (JSON → JSON → JSON) → JSON → JSON → JSON\n```\n\nFlips the argument order of a function of two or more arguments\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisdone-archive%2Fjl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrisdone-archive%2Fjl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisdone-archive%2Fjl/lists"}