Skip to content

Commit

Permalink
improve readme, clean-up
Browse files Browse the repository at this point in the history
  • Loading branch information
peterbecich committed Jan 4, 2024
1 parent 3be53bf commit c2fff2a
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 110 deletions.
134 changes: 71 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,76 +1,84 @@
# purescript-bridge


[![Haskell library and example](https://github.com/eskimor/purescript-bridge/actions/workflows/haskell.yml/badge.svg)](https://github.com/eskimor/purescript-bridge/actions/workflows/haskell.yml) [![Purescript example](https://github.com/eskimor/purescript-bridge/actions/workflows/purescript.yml/badge.svg)](https://github.com/eskimor/purescript-bridge/actions/workflows/purescript.yml) [![Nix Flake](https://github.com/eskimor/purescript-bridge/actions/workflows/nix-flake.yml/badge.svg)](https://github.com/eskimor/purescript-bridge/actions/workflows/nix-flake.yml)


[![Haskell library and example](https://github.com/eskimor/purescript-bridge/actions/workflows/haskell.yml/badge.svg)](https://github.com/eskimor/purescript-bridge/actions/workflows/haskell.yml) [![PureScript example](https://github.com/eskimor/purescript-bridge/actions/workflows/purescript.yml/badge.svg)](https://github.com/eskimor/purescript-bridge/actions/workflows/purescript.yml) [![Nix Flake](https://github.com/eskimor/purescript-bridge/actions/workflows/nix-flake.yml/badge.svg)](https://github.com/eskimor/purescript-bridge/actions/workflows/nix-flake.yml)

Translate your Haskell types to PureScript types. It should in theory work for almost all Haskell types, including type constructors!
You just have to instantiate it with dummy parameters from e.g. "Language.PureScript.Bridge.TypeParameters".

Data type translation is fully and easily customizable by providing your own `BridgePart` instances!

The latest version of this project requires **Purescript 0.15**.
The latest version of this project requires **PureScript 0.15**.

## JSON encoding / decoding
### Haskell
Use [`aeson`](http://hackage.haskell.org/package/aeson)'s generic encoding/decoding with default options

### PureScript
There are two PureScript libraries which can interface with Aeson through PureScript bridge. The second, `purescript-argonaut-aeson-generic`, has issues.

#### [`input-output-hk/purescript-bridge-json-helpers`](https://github.com/input-output-hk/purescript-bridge-json-helpers.git)

Enable this on the Haskell side with `Language.PureScript.Bridge.SumType.jsonHelpers`.

* see `./test/RoundTripJsonHelpers` for example
* sample Dhall config (for [spago-legacy](https://github.com/purescript/spago-legacy)):
```
, json-helpers =
{ dependencies =
[ "aff"
, "argonaut-codecs"
, "argonaut-core"
, "arrays"
, "bifunctors"
, "contravariant"
, "control"
, "effect"
, "either"
, "enums"
, "foldable-traversable"
, "foreign-object"
, "maybe"
, "newtype"
, "ordered-collections"
, "prelude"
, "profunctor"
, "psci-support"
, "quickcheck"
, "record"
, "spec"
, "spec-quickcheck"
, "transformers"
, "tuples"
, "typelevel-prelude"
]
, repo =
"https://github.com/input-output-hk/purescript-bridge-json-helpers.git"
, version = "486db9ee62882baa42cca24f556848c5f6bec565"
}
```

#### [`purescript-argonaut-aeson-generic >=0.4.1`](https://pursuit.purescript.org/packages/purescript-argonaut-aeson-generic/0.4.1) ([GitHub](https://github.com/coot/purescript-argonaut-aeson-generic))

Enable this on the Haskell side with `Language.PureScript.Bridge.SumType.argonautAesonGeneric`.

For compatible JSON representations:

* On Haskell side, use:
* Use [`aeson`](http://hackage.haskell.org/package/aeson)'s generic encoding/decoding with default options
* On Purescript side, use:
* [`purescript-argonaut-aeson-generic >=0.4.1`](https://pursuit.purescript.org/packages/purescript-argonaut-aeson-generic/0.4.1) ([GitHub](https://github.com/coot/purescript-argonaut-aeson-generic))
* additional requirement [`peterbecich/purescript-argonaut-codecs`](https://github.com/peterbecich/purescript-argonaut-codecs.git)
* commit `04abb3eb24a4deafe125be0eb23e2786c642e66b`
* see `./test/RoundTripArgonautAesonGeneric` for example
* sample Dhall config:
```
, argonaut-codecs =
{ dependencies = [ "console" ]
, repo = "https://github.com/peterbecich/purescript-argonaut-codecs.git"
, version = "04abb3eb24a4deafe125be0eb23e2786c642e66b"
}
```
* forked from [`purescript-contrib/purescript-argonaut-codecs`](https://github.com/purescript-contrib/purescript-argonaut-codecs)
* [discussion](https://github.com/purescript-contrib/purescript-argonaut-codecs/issues/115)
* *or* [`input-output-hk/purescript-bridge-json-helpers`](https://github.com/input-output-hk/purescript-bridge-json-helpers.git)
* commit `486db9ee62882baa42cca24f556848c5f6bec565`
* see `./test/RoundTripJsonHelpers` for example
* sample Dhall config:
```
, json-helpers =
{ dependencies =
[ "aff"
, "argonaut-codecs"
, "argonaut-core"
, "arrays"
, "bifunctors"
, "contravariant"
, "control"
, "effect"
, "either"
, "enums"
, "foldable-traversable"
, "foreign-object"
, "maybe"
, "newtype"
, "ordered-collections"
, "prelude"
, "profunctor"
, "psci-support"
, "quickcheck"
, "record"
, "spec"
, "spec-quickcheck"
, "transformers"
, "tuples"
, "typelevel-prelude"
]
, repo =
"https://github.com/input-output-hk/purescript-bridge-json-helpers.git"
, version = "486db9ee62882baa42cca24f556848c5f6bec565"
}
```
This library is demonstrated by the `example`; see `./example/readme.md`.

**TODO**: [resolve incompatibility between Argonaut and Aeson](https://github.com/purescript-contrib/purescript-argonaut-codecs/issues/115)

**Additional requirement**: [`peterbecich/purescript-argonaut-codecs`](https://github.com/peterbecich/purescript-argonaut-codecs.git)
* commit `04abb3eb24a4deafe125be0eb23e2786c642e66b`
* see `./test/RoundTripArgonautAesonGeneric` for example
* note that some types have been disabled from the `RoundTripArgonautAesonGeneric` test
* `RoundTripJsonHelpers` tests more types
* the types tested can be expanded when the incompatibility issue is resolved
* sample Dhall config:
```
, argonaut-codecs =
{ dependencies = [ "console" ]
, repo = "https://github.com/peterbecich/purescript-argonaut-codecs.git"
, version = "04abb3eb24a4deafe125be0eb23e2786c642e66b"
}
```
* forked from [`purescript-contrib/purescript-argonaut-codecs`](https://github.com/purescript-contrib/purescript-argonaut-codecs)

## Documentation

Expand Down
17 changes: 9 additions & 8 deletions example/readme.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
# Purescript Bridge example
# PureScript Bridge example

This project demonstrates the libraries Purescript Bridge and [`input-output-hk/purescript-bridge-json-helpers`](https://github.com/input-output-hk/purescript-bridge-json-helpers.git).
This project demonstrates the libraries PureScript Bridge and [`purescript-argonaut-aeson-generic`](https://pursuit.purescript.org/packages/purescript-argonaut-aeson-generic) ([GitHub](https://github.com/coot/purescript-argonaut-aeson-generic))

It does not use [`purescript-argonaut-aeson-generic`](https://pursuit.purescript.org/packages/purescript-argonaut-aeson-generic) ([GitHub](https://github.com/coot/purescript-argonaut-aeson-generic)).
It does not use [`input-output-hk/purescript-bridge-json-helpers`](https://github.com/input-output-hk/purescript-bridge-json-helpers.git).
To demonstrate this library in the example, more work is needed in `Main.purs`.

The Haskell type `Foo`, in `src/Types.hs`, is generated for Purescript by Purescript Bridge. `purescript-bridge-json-helpers` is used to decode and encode this type, client-side.
The Haskell type `Foo`, in `src/Types.hs`, is generated for PureScript by PureScript Bridge. Some of values in `Foo` are randomly generated every time the page is loaded. `purescript-argonaut-aeson-generic` is used to decode and encode this payload, client-side. The client modifies some of the payload's values and sends it back to the server.

# Dependencies
## Nix
The `nix develop` shell will provide Purescipt 0.15 and Spago.
The `nix develop` shell will provide PureScript 0.15 and Spago.
## Without Nix
You must install Purescript 0.15 and Spago.
You must install PureScript 0.15 and [spago-legacy](https://github.com/purescript/spago-legacy).

# Running the example
- Enter the `example` directory
Expand Down Expand Up @@ -38,10 +39,10 @@ Foo message: Hello Foo number: 123 Foo list length: 11
Foo message: Hola Foo number: 124 Foo list length: 22
```

# Updating the Purescript Bridge
# Updating the PureScript Bridge
- Enter the `example` directory

- Regenerate the Purescript Bridge types:
- Regenerate the PureScript Bridge types:
```
cabal run generate-purescript
```
Expand Down
2 changes: 1 addition & 1 deletion example/src/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ testFoo = Foo
}

main :: Effect Unit
main = log "Hello, Purescript!" *> launchAff_ do
main = log "Hello, PureScript!" *> launchAff_ do
-- request a Foo
fooResponse <- get json "/foo"
for_ fooResponse \fooPayload -> do
Expand Down
5 changes: 2 additions & 3 deletions example/src/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,8 @@ myBridge = defaultBridge
additionalInstances = lenses
. genericShow
. argonautAesonGeneric

-- . jsonHelper
-- To use json-helpers with the example, a modification is necessary
-- . jsonHelpers
-- To use json-helpers with the example, more work is needed
-- in Main.purs

myTypes :: [SumType 'Haskell]
Expand Down
2 changes: 1 addition & 1 deletion example/static/index.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!doctype html>
<html>
<head>
<title>Purescript Bridge Example</title>
<title>PureScript Bridge Example</title>
<style>body {
font-family: sans-serif;
max-width: 800px;
Expand Down
9 changes: 2 additions & 7 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,10 @@
};
devShell = {
enable = true;
mkShellArgs = {
# shellHook = ''
# export LD_LIBRARY_PATH=${pkgs.zlib.out}/lib:LD_LIBRARY_PATH
# '';
};
tools = haskellPackages: {
# disable until haskell-language-server compatible with Aeson 2.2
# disable until NixPkgs-available Floskell compatible with Aeson 2.2
haskell-language-server = null;
inherit (haskellPackages) zlib;
inherit (haskellPackages) zlib stylish-haskell;
};
hlsCheck.enable = false;
};
Expand Down
4 changes: 2 additions & 2 deletions src/Language/PureScript/Bridge.hs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ import Language.PureScript.Bridge.SumType as Bridge (CustomInstance (.
getUsedTypes,
importsFromList,
instanceToImportLines,
jsonHelper,
jsonHelpers,
lenses,
memberBindings,
memberBody,
Expand Down Expand Up @@ -195,7 +195,7 @@ import Language.PureScript.Bridge.TypeInfo as Bridge (HasHaskType (..)
> data Bar = A | B | C deriving (Eq, Ord, Generic)
> data Baz = ... deriving (Generic)
>
> -- | All types will have a `Generic` instance produced in Purescript.
> -- | All types will have a `Generic` instance produced in PureScript.
> myTypes :: [SumType 'Haskell]
> myTypes =
> [ equal (mkSumType @Foo) -- Also produce a `Eq` instance.
Expand Down
4 changes: 2 additions & 2 deletions src/Language/PureScript/Bridge/Printer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ constructorToDoc (DataConstructor n args) =
Normal ts -> NE.toList $ typeInfoToDoc <$> ts
Record rs -> [vrecord $ fieldSignatures rs]

{- | Given a Purescript type, generate instances for typeclass instances it claims to have.
{- | Given a PureScript type, generate instances for typeclass instances it claims to have.
-}
instances :: SumType 'PureScript -> [Doc]
instances st@(SumType t _ is) = go <$> is
Expand Down Expand Up @@ -363,7 +363,7 @@ instances st@(SumType t _ is) = go <$> is
decodeJsonConstraints
["decodeJson = defer \\_ -> genericDecodeAeson Argonaut.defaultOptions"]
{-|
This relies on unpublished Purescript library `purescript-bridge-json-helpers`:
This relies on unpublished PureScript library `purescript-bridge-json-helpers`:
https://github.com/input-output-hk/purescript-bridge-json-helpers
and `purescript-argonaut-codecs`
https://pursuit.purescript.org/packages/purescript-argonaut-codecs
Expand Down
23 changes: 11 additions & 12 deletions src/Language/PureScript/Bridge/SumType.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeSynonymInstances #-}

module Language.PureScript.Bridge.SumType
( SumType (..)
Expand All @@ -20,7 +19,7 @@ module Language.PureScript.Bridge.SumType
, equal1
, order
, argonautAesonGeneric
, jsonHelper
, jsonHelpers
, genericShow
, functor
, DataConstructor (..)
Expand Down Expand Up @@ -64,7 +63,7 @@ import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NE
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe (fromMaybe, maybeToList)
import Data.Maybe (maybeToList)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Text (Text)
Expand Down Expand Up @@ -125,7 +124,7 @@ mkSumType =
where
constructors = gToConstructors (from (undefined :: t))

-- | Purescript typeclass instances that can be generated for your Haskell types.
-- | PureScript typeclass instances that can be generated for your Haskell types.
data Instance (lang :: Language)
= Generic
| GenericShow
Expand All @@ -139,11 +138,11 @@ data Instance (lang :: Language)
-- using argonaut-codecs:
-- <https://pursuit.purescript.org/packages/purescript-argonaut-codecs>
| DecodeJson
-- | Generate using unpublished Purescript library
-- | Generate using unpublished PureScript library
-- `purescript-bridge-json-helpers`
-- <https://github.com/input-output-hk/purescript-bridge-json-helpers>
| EncodeJsonHelper
-- | Generate using unpublished Purescript library
-- | Generate using unpublished PureScript library
-- `purescript-bridge-json-helpers`
-- <https://github.com/input-output-hk/purescript-bridge-json-helpers>
| DecodeJsonHelper
Expand Down Expand Up @@ -194,7 +193,7 @@ data CustomInstance (lang :: Language) = CustomInstance
}
deriving (Eq, Ord, Show)

{- | The Purescript typeclass `Newtype` might be derivable if the original
{- | The PureScript typeclass `Newtype` might be derivable if the original
Haskell type was a simple type wrapper.
-}
nootype :: [DataConstructor lang] -> Maybe (Instance lang)
Expand All @@ -211,8 +210,8 @@ argonautAesonGeneric (SumType ti dc is) = SumType ti dc . nub $ EncodeJson : Dec
-- | Ensure that aeson-compatible `EncodeJson` and `DecodeJson` instances are generated for your type.
-- Uses unpublished library `purescript-bridge-json-helpers`
-- <https://github.com/input-output-hk/purescript-bridge-json-helpers>
jsonHelper :: SumType t -> SumType t
jsonHelper (SumType ti dc is) = SumType ti dc . nub $ EncodeJsonHelper : DecodeJsonHelper : is
jsonHelpers :: SumType t -> SumType t
jsonHelpers (SumType ti dc is) = SumType ti dc . nub $ EncodeJsonHelper : DecodeJsonHelper : is

-- | Ensure that a generic `Show` instance is generated for your type.
genericShow :: SumType t -> SumType t
Expand Down Expand Up @@ -332,7 +331,7 @@ instanceToTypes DecodeJson = fmap constraintToType
-- , TypeInfo "purescript-argonaut-aeson-generic" "Data.Argonaut.Aeson.Decode.Generic" "genericDecodeAeson" []
]
{-|
For unpublished Purescript library `purescript-bridge-json-helpers`:
For unpublished PureScript library `purescript-bridge-json-helpers`:
https://github.com/input-output-hk/purescript-bridge-json-helpers
and `purescript-argonaut-codecs`
https://pursuit.purescript.org/packages/purescript-argonaut-codecs
Expand Down Expand Up @@ -414,7 +413,7 @@ instanceToImportLines DecodeJson =
, ImportLine "Control.Lazy" Nothing $ Set.fromList ["defer"]
]
{-|
This relies on unpublished Purescript library `purescript-bridge-json-helpers`:
This relies on unpublished PureScript library `purescript-bridge-json-helpers`:
<https://github.com/input-output-hk/purescript-bridge-json-helpers>
and `purescript-argonaut-codecs`
<https://pursuit.purescript.org/packages/purescript-argonaut-codecs>
Expand All @@ -431,7 +430,7 @@ instanceToImportLines EncodeJsonHelper =
]
<> instanceToImportLines EncodeJson
{-|
This relies on unpublished Purescript library `purescript-bridge-json-helpers`:
This relies on unpublished PureScript library `purescript-bridge-json-helpers`:
<https://github.com/input-output-hk/purescript-bridge-json-helpers>
and `purescript-argonaut-codecs`
<https://pursuit.purescript.org/packages/purescript-argonaut-codecs>
Expand Down
11 changes: 6 additions & 5 deletions test/RoundTripArgonautAesonGeneric/Spec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

module RoundTripArgonautAesonGeneric.Spec where
module RoundTripArgonautAesonGeneric.Spec (roundtripSpec)
where

import Control.Exception (bracket)
import Data.Aeson (FromJSON, ToJSON (toJSON), eitherDecode, encode,
Expand All @@ -17,7 +18,7 @@ import GHC.Generics (Generic)
import Language.PureScript.Bridge (BridgePart, Language (..), SumType,
argonautAesonGeneric, buildBridge,
defaultBridge, equal, functor,
genericShow, jsonHelper, mkSumType,
genericShow, jsonHelpers, mkSumType,
order, writePSTypes,
writePSTypesWith)
import Language.PureScript.Bridge.TypeParameters (A)
Expand Down Expand Up @@ -91,12 +92,12 @@ roundtripSpec = do
err <- hGetLine herr
output <- hGetLine hout

-- empty string signifies no error from Purescript process
-- assertEqual ("Error from Purescript, parsing: " <> input) "" err
-- empty string signifies no error from PureScript process
-- assertEqual ("Error from PureScript, parsing: " <> input) "" err

-- compare the value parsed by Purescipt to the
-- source value in Haskell
assertEqual ("Mismatch between value sent to Purescript and value returned: " <> output) (Right testData)
assertEqual ("Mismatch between value sent to PureScript and value returned: " <> output) (Right testData)
. eitherDecode @TestData
$ fromString output

Expand Down
Loading

0 comments on commit c2fff2a

Please sign in to comment.