Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/chrisdone-archive/dynamic
Dynamic typing in Haskell
https://github.com/chrisdone-archive/dynamic
analytics dynamic-typing enterprise-software haskell
Last synced: 24 days ago
JSON representation
Dynamic typing in Haskell
- Host: GitHub
- URL: https://github.com/chrisdone-archive/dynamic
- Owner: chrisdone-archive
- License: bsd-3-clause
- Archived: true
- Created: 2019-03-31T15:33:14.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2019-05-29T23:05:25.000Z (over 5 years ago)
- Last Synced: 2024-11-29T11:10:41.979Z (25 days ago)
- Topics: analytics, dynamic-typing, enterprise-software, haskell
- Language: Haskell
- Size: 29.3 KB
- Stars: 192
- Watchers: 7
- Forks: 5
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# dynamic
Finally, dynamically typed programming in Haskell made easy!
## Introduction
Tired of making data types in your Haskell programs just to read and
manipulate basic JSON/CSV files? Tired of writing imports? Use
`dynamic`, dynamically typed programming for Haskell!## Load it up
Launch `ghci`, the interactive REPL for Haskell.
``` haskell
import Dynamic
```Don't forget to enable `OverloadedStrings`:
``` haskell
:set -XOverloadedStrings
```Now you're ready for dynamicness!
## The Dynamic type
In the `dynamic` package there is one type: `Dynamic`!
What, you were expecting something more? Guffaw!
## Make dynamic values as easy as pie!
Primitive values are easy via regular literals:
``` haskell
> 1
1
> "Hello, World!"
"Hello, World!"
```Arrays and objects have handy functions to make them:
``` haskell
> fromList [1,2]
[
1,
2
]
> fromDict [ ("k", 1), ("v", 2) ]
{
"k": 1,
"v": 2
}
```Get object keys or array or string indexes via `!`:
``` haskell
> fromDict [ ("k", 1), ("v", 2) ] ! "k"
1
> fromList [1,2] ! 1
2
> "foo" ! 2
"o"
```## Web requests!
```json
> chris <- getJson "https://api.github.com/users/chrisdone" []
> chris
{
"bio": null,
"email": null,
"public_gists": 176,
"repos_url": "https://api.github.com/users/chrisdone/repos",
"node_id": "MDQ6VXNlcjExMDE5",
"following_url": "https://api.github.com/users/chrisdone/following{/other_user}",
"location": "England",
"url": "https://api.github.com/users/chrisdone",
"gravatar_id": "",
"blog": "https://chrisdone.com",
"gists_url": "https://api.github.com/users/chrisdone/gists{/gist_id}",
"following": 0,
"hireable": null,
"organizations_url": "https://api.github.com/users/chrisdone/orgs",
"subscriptions_url": "https://api.github.com/users/chrisdone/subscriptions",
"name": "Chris Done",
"company": "FP Complete @fpco ",
"updated_at": "2019-02-22T11:11:18Z",
"created_at": "2008-05-21T10:29:09Z",
"followers": 1095,
"id": 11019,
"public_repos": 144,
"avatar_url": "https://avatars3.githubusercontent.com/u/11019?v=4",
"type": "User",
"events_url": "https://api.github.com/users/chrisdone/events{/privacy}",
"starred_url": "https://api.github.com/users/chrisdone/starred{/owner}{/repo}",
"login": "chrisdone",
"received_events_url": "https://api.github.com/users/chrisdone/received_events",
"site_admin": false,
"html_url": "https://github.com/chrisdone",
"followers_url": "https://api.github.com/users/chrisdone/followers"
}
```## Trivially read CSV files!
``` haskell
> fromCsvNamed "name,age,alive,partner\nabc,123,true,null\nabc,ok,true,true"
[{
"alive": true,
"age": 123,
"partner": null,
"name": "abc"
},{
"alive": true,
"age": "ok",
"partner": true,
"name": "abc"
}]
```## Dynamically typed programming!
Just write code like you do in Python or JavaScript:
```haskell
> if chris!"followers" > 500 then chris!"public_gists" * 5 else chris!"name"
880
```## Experience the wonders of dynamic type errors!
Try to treat non-numbers as numbers and you get the expected result:
``` haskell
> map (\o -> o ! "age" * 2) $ fromCsvNamed "name,age,alive,partner\nabc,123,true,null\nabc,ok,true,true"
[246,*** Exception: DynamicTypeError "Couldn't treat string as number: ok"
```Laziness makes everything better!
``` haskell
> map (*2) $ toList $ fromJson "[\"1\",true,123]"
[2,*** Exception: DynamicTypeError "Can't treat bool as number."
```Woops...
``` haskell
> map (*2) $ toList $ fromJson "[\"1\",123]"
[2,246]
```That's better!
Heterogenous lists are what life is about:
``` haskell
> toCsv [ 1, "Chris" ]
"1.0\r\nChris\r\n"
```I can't handle it!!!
## Modifying and updating records
Use `modify` or `set` to massage data into something more palatable.
``` haskell
> modify "followers" (*20) chris
{
"bio": null,
"email": null,
"public_gists": 176,
"repos_url": "https://api.github.com/users/chrisdone/repos",
"node_id": "MDQ6VXNlcjExMDE5",
"following_url": "https://api.github.com/users/chrisdone/following{/other_user}",
"location": "England",
"url": "https://api.github.com/users/chrisdone",
"gravatar_id": "",
"blog": "https://chrisdone.com",
"gists_url": "https://api.github.com/users/chrisdone/gists{/gist_id}",
"following": 0,
"hireable": null,
"organizations_url": "https://api.github.com/users/chrisdone/orgs",
"subscriptions_url": "https://api.github.com/users/chrisdone/subscriptions",
"name": "Chris Done",
"company": "FP Complete @fpco ",
"updated_at": "2019-02-22T11:11:18Z",
"created_at": "2008-05-21T10:29:09Z",
"followers": 21900,
"id": 11019,
"public_repos": 144,
"avatar_url": "https://avatars3.githubusercontent.com/u/11019?v=4",
"type": "User",
"events_url": "https://api.github.com/users/chrisdone/events{/privacy}",
"starred_url": "https://api.github.com/users/chrisdone/starred{/owner}{/repo}",
"login": "chrisdone",
"received_events_url": "https://api.github.com/users/chrisdone/received_events",
"site_admin": false,
"html_url": "https://github.com/chrisdone",
"followers_url":
"https://api.github.com/users/chrisdone/followers"
}
```## List of numbers?
The answer is: Yes, Haskell can do that!
``` haskell
> [1.. 5] :: [Dynamic]
[1,2,3,4,5]
```## Append things together
Like in JavaScript, we try to do our best to make something out of appending...
``` haskell
> "Wat" <> 1 <> "!" <> Null
"Wat1!"
```## Suspicious?
It's real! This code runs just fine:
``` haskell
silly a =
if a > 0
then toJsonFile "out.txt" "Hi"
else toJsonFile "out.txt" (5 + "a")
```That passes [the dynamic typing test](https://stackoverflow.com/a/27791387).
## Mix and match your regular Haskell functions
Here's an exporation of my Monzo (bank account) data.
Load up the JSON output:
```haskell
> monzo <- fromJsonFile "monzo.json"
```Preview what's in it:
```haskell
> take 100 $ show monzo
"{\n \"transactions\": [\n {\n \"amount\": 10000,\n \"dedupe_id\": \"com.monzo.f"
> toKeys monzo
["transactions"]
```OK, just transactions. How many?
```haskell
> length $ toList $ monzo!"transactions"
119
```What keys do I get in each transaction?
```haskell
> toKeys $ head $ toList $ monzo!"transactions"
["amount","dedupe_id","attachments","can_be_made_subscription","fees","created","category","settled","can_split_the_bill","can_add_to_tab","originator","currency","include_in_spending","merchant","can_be_excluded_from_breakdown","international","counterparty","scheme","local_currency","metadata","id","labels","updated","account_balance","is_load","account_id","notes","user_id","local_amount","description"]
```What's in `amount`?
```haskell
> (!"amount") $ head $ toList $ monzo!"transactions"
10000
```Looks like pennies, let's divide that by 100. What's the total +/- sum
of my last 5 transactions?```haskell
> sum $ map ((/100) . (!"amount")) $ take 5 $ toList $ monzo!"transactions"
468.65
```What categories are there?
```haskell
> nub $ map (!"category") $ toList $ monzo!"transactions"
["general","entertainment","groceries","eating_out","shopping","expenses","bills","personal_care","cash"]
```How many transactions did I do in each category? Let's use Data.Map to
histogram that.```haskell
> fromDict $ M.toList $ foldl (\cats cat -> M.insertWith (+) cat 1 cats) mempty $ map (!"category") $ toList $ monzo!"transactions"
{
"personal_care": 2,
"entertainment": 8,
"bills": 3,
"general": 58,
"groceries": 16,
"shopping": 8,
"expenses": 19,
"eating_out": 4,
"cash": 1
}
>
```Cool!