From 9a139ca0395a13216eda2b82a6802f34c330324c Mon Sep 17 00:00:00 2001 From: jtomas Date: Mon, 28 Mar 2022 15:35:23 +0200 Subject: [PATCH 1/3] es modules --- .eslintrc.json | 26 +++++++++++++++++++++ bower.json | 18 +++++++-------- package.json | 10 ++++---- src/Foreign/Generic/Class.purs | 38 ++++++++++++++++--------------- src/Foreign/Generic/Enum.purs | 7 +++--- src/Foreign/Internal/Stringify.js | 4 +--- src/Foreign/JSON.js | 6 +---- src/Foreign/NullOrUndefined.js | 5 ++-- src/Foreign/NullOrUndefined.purs | 4 ++-- 9 files changed, 70 insertions(+), 48 deletions(-) create mode 100644 .eslintrc.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..1c6afb9 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,26 @@ +{ + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "extends": "eslint:recommended", + "rules": { + "strict": [2, "global"], + "block-scoped-var": 2, + "consistent-return": 2, + "eqeqeq": [2, "smart"], + "guard-for-in": 2, + "no-caller": 2, + "no-extend-native": 2, + "no-loop-func": 2, + "no-new": 2, + "no-param-reassign": 2, + "no-return-assign": 2, + "no-unused-expressions": 2, + "no-use-before-define": 2, + "radix": [2, "always"], + "indent": [2, 2], + "quotes": [2, "double"], + "semi": [2, "always"] + } +} diff --git a/bower.json b/bower.json index 3507559..7124e4a 100644 --- a/bower.json +++ b/bower.json @@ -15,16 +15,16 @@ "url": "git://github.com/paf31/purescript-foreign-generic.git" }, "dependencies": { - "purescript-effect": "^3.0.0", - "purescript-foreign": "^6.0.0", - "purescript-foreign-object": "^3.0.0", - "purescript-ordered-collections": "^2.0.0", - "purescript-exceptions": "^5.0.0", - "purescript-record": "^3.0.0", - "purescript-identity": "^5.0.0" + "purescript-effect": "master", + "purescript-foreign": "master", + "purescript-foreign-object": "master", + "purescript-ordered-collections": "master", + "purescript-exceptions": "master", + "purescript-record": "master", + "purescript-identity": "master" }, "devDependencies": { - "purescript-assert": "^5.0.0", - "purescript-psci-support": "^5.0.0" + "purescript-assert": "master", + "purescript-psci-support": "master" } } diff --git a/package.json b/package.json index 38e6586..a1d6811 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,13 @@ "private": true, "scripts": { "clean": "rimraf output && rimraf .pulp-cache", - "build": "pulp build -- --censor-lib --strict", + "build": "eslint src && pulp build -- --censor-lib --strict", "test": "pulp test" }, "devDependencies": { - "pulp": "^15.0.0", - "purescript": "^0.14.0", - "purescript-psa": "^0.5.0", - "rimraf": "^2.5.0" + "eslint": "^7.15.0", + "pulp": "16.0.0-0", + "purescript-psa": "^0.8.2", + "rimraf": "^3.0.2" } } diff --git a/src/Foreign/Generic/Class.purs b/src/Foreign/Generic/Class.purs index 9370082..582c851 100644 --- a/src/Foreign/Generic/Class.purs +++ b/src/Foreign/Generic/Class.purs @@ -13,12 +13,13 @@ import Data.List (List(..), (:)) import Data.List as List import Data.Maybe (Maybe(..), maybe) import Data.Newtype (unwrap) -import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) +import Data.Symbol (class IsSymbol, reflectSymbol) import Data.Traversable (sequence) import Foreign (F, Foreign, ForeignError(..), fail, readArray, readBoolean, readChar, readInt, readNumber, readString, unsafeToForeign) import Foreign.Generic.Internal (readObject) import Foreign.Index (index) -import Foreign.NullOrUndefined (readNullOrUndefined, null) +import Foreign.NullOrUndefined (readNullOrUndefined, aNull) + import Foreign.Object (Object) import Foreign.Object as Object import Prim.Row (class Cons, class Lacks) @@ -26,7 +27,6 @@ import Prim.RowList (class RowToList, Nil, Cons) import Record as Record import Record.Builder (Builder) import Record.Builder as Builder -import Type.Data.RowList (RLProxy(..)) import Type.Proxy (Proxy(..)) import Unsafe.Coerce (unsafeCoerce) @@ -181,7 +181,7 @@ instance arrayEncode :: Encode a => Encode (Array a) where encode = unsafeToForeign <<< map encode instance maybeEncode :: Encode a => Encode (Maybe a) where - encode = maybe null encode + encode = maybe aNull encode instance objectEncode :: Encode v => Encode (Object v) where encode = unsafeToForeign <<< Object.mapWithKey (\_ -> encode) @@ -206,20 +206,22 @@ class EncodeWithOptions a where encodeWithOptions :: Options -> a -> Foreign instance decodeWithOptionsRecord :: (RowToList r rl, DecodeRecord r rl) => DecodeWithOptions (Record r) where - decodeWithOptions opts = map (flip Builder.build {}) <$> decodeRecordWithOptions (RLProxy :: RLProxy rl) opts + decodeWithOptions opts = map (flip Builder.build {}) <$> decodeRecordWithOptions (Proxy :: Proxy rl) opts else instance decodeWithOptionsOther :: Decode a => DecodeWithOptions a where decodeWithOptions _ = decode instance encodeWithOptionsRecord :: (RowToList r rl, EncodeRecord r rl) => EncodeWithOptions (Record r) where - encodeWithOptions opts = unsafeToForeign <<< encodeRecordWithOptions (RLProxy :: RLProxy rl) opts + encodeWithOptions opts = unsafeToForeign <<< encodeRecordWithOptions (Proxy :: Proxy rl) opts else instance encodeWithOptionsOther :: Encode a => EncodeWithOptions a where encodeWithOptions _ = encode +class DecodeRecord :: forall k. Row Type -> k -> Constraint class DecodeRecord r rl | rl -> r where - decodeRecordWithOptions :: RLProxy rl -> Options -> Foreign -> F (Builder {} (Record r)) + decodeRecordWithOptions :: Proxy rl -> Options -> Foreign -> F (Builder {} (Record r)) +class EncodeRecord :: forall k. Row Type -> k -> Constraint class EncodeRecord r rl | rl -> r where - encodeRecordWithOptions :: RLProxy rl -> Options -> Record r -> Object Foreign + encodeRecordWithOptions :: Proxy rl -> Options -> Record r -> Object Foreign instance decodeRecordNil :: DecodeRecord () Nil where decodeRecordWithOptions _ _ _ = pure identity @@ -237,12 +239,12 @@ instance decodeRecordCons => DecodeRecord r (Cons l a rl_) where decodeRecordWithOptions _ opts f = do - builder <- decodeRecordWithOptions (RLProxy :: RLProxy rl_) opts f - let l = reflectSymbol (SProxy :: SProxy l) + builder <- decodeRecordWithOptions (Proxy :: Proxy rl_) opts f + let l = reflectSymbol (Proxy :: Proxy l) l_transformed = (opts.fieldTransform l) f_ <- index f l_transformed a <- mapExcept (lmap (map (ErrorAtProperty l_transformed))) (decodeWithOptions opts f_) - pure (builder >>> Builder.insert (SProxy :: SProxy l) a) + pure (builder >>> Builder.insert (Proxy :: Proxy l) a) instance encodeRecordCons :: ( Cons l a r_ r @@ -253,9 +255,9 @@ instance encodeRecordCons => EncodeRecord r (Cons l a rl_) where encodeRecordWithOptions _ opts rec = - let obj = encodeRecordWithOptions (RLProxy :: RLProxy rl_) opts (unsafeCoerce rec) - l = reflectSymbol (SProxy :: SProxy l) - in Object.insert (opts.fieldTransform l) (encodeWithOptions opts (Record.get (SProxy :: SProxy l) rec)) obj + let obj = encodeRecordWithOptions (Proxy :: Proxy rl_) opts (unsafeCoerce rec) + l = reflectSymbol (Proxy :: Proxy l) + in Object.insert (opts.fieldTransform l) (encodeWithOptions opts (Record.get (Proxy :: Proxy l) rec)) obj class GenericDecode a where decodeOpts :: Options -> Foreign -> F a @@ -276,7 +278,7 @@ class GenericCountArgs a where countArgs :: Proxy a -> Either a Int instance genericDecodeNoConstructors :: GenericDecode NoConstructors where - decodeOpts opts _ = fail (ForeignError "No constructors") + decodeOpts _ _ = fail (ForeignError "No constructors") instance genericEncodeNoConstructors :: GenericEncode NoConstructors where encodeOpts opts a = encodeOpts opts a @@ -289,7 +291,7 @@ instance genericDecodeConstructor then Constructor <$> readArguments f else case opts.sumEncoding of TaggedObject { tagFieldName, contentsFieldName, constructorTagTransform } -> do - tag <- mapExcept (lmap (map (ErrorAtProperty tagFieldName))) do + _ <- mapExcept (lmap (map (ErrorAtProperty tagFieldName))) do tag <- index f tagFieldName >>= readString let expected = constructorTagTransform ctorName unless (tag == expected) $ @@ -299,7 +301,7 @@ instance genericDecodeConstructor (index f contentsFieldName >>= readArguments) pure (Constructor args) where - ctorName = reflectSymbol (SProxy :: SProxy name) + ctorName = reflectSymbol (Proxy :: Proxy name) numArgs = countArgs (Proxy :: Proxy rep) @@ -329,7 +331,7 @@ instance genericEncodeConstructor unsafeToForeign (Object.singleton tagFieldName (unsafeToForeign $ constructorTagTransform ctorName) `Object.union` maybe Object.empty (Object.singleton contentsFieldName) (encodeArgsArray args)) where - ctorName = reflectSymbol (SProxy :: SProxy name) + ctorName = reflectSymbol (Proxy :: Proxy name) encodeArgsArray :: rep -> Maybe Foreign encodeArgsArray = unwrapArguments <<< List.toUnfoldable <<< encodeArgs opts diff --git a/src/Foreign/Generic/Enum.purs b/src/Foreign/Generic/Enum.purs index 5a8381f..3ac7397 100644 --- a/src/Foreign/Generic/Enum.purs +++ b/src/Foreign/Generic/Enum.purs @@ -4,10 +4,11 @@ import Prelude import Control.Alt ((<|>)) import Data.Generic.Rep (class Generic, Argument, Constructor(..), NoArguments(..), Product, Sum(..), from, to) -import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) +import Data.Symbol (class IsSymbol, reflectSymbol) import Foreign (F, Foreign, ForeignError(..), fail, readString, unsafeToForeign) import Partial.Unsafe (unsafeCrashWith) import Prim.TypeError (class Fail, Text) +import Type.Proxy (Proxy(..)) type GenericEnumOptions = { constructorTagTransform :: String -> String @@ -77,7 +78,7 @@ instance ctorNoArgsGenericDecodeEnum fail (ForeignError ("Expected " <> show ctorName <> " tag for unary constructor literal " <> ctorName)) pure $ Constructor NoArguments where - ctorName = constructorTagTransform $ reflectSymbol (SProxy :: SProxy name) + ctorName = constructorTagTransform $ reflectSymbol (Proxy :: Proxy name) instance ctorArgumentGenericDecodeEnum :: Fail (Text "genericEncode/DecodeEnum cannot be used on types that are not sums of constructors with no arguments.") @@ -100,7 +101,7 @@ instance ctorNoArgsGenericEncodeEnum => GenericEncodeEnum (Constructor name NoArguments) where encodeEnum {constructorTagTransform} _ = unsafeToForeign ctorName where - ctorName = constructorTagTransform $ reflectSymbol (SProxy :: SProxy name) + ctorName = constructorTagTransform $ reflectSymbol (Proxy :: Proxy name) instance ctorArgumentGenericEncodeEnum :: Fail (Text "genericEncode/DecodeEnum cannot be used on types that are not sums of constructors with no arguments.") diff --git a/src/Foreign/Internal/Stringify.js b/src/Foreign/Internal/Stringify.js index 97f76db..5ce2dc8 100644 --- a/src/Foreign/Internal/Stringify.js +++ b/src/Foreign/Internal/Stringify.js @@ -1,3 +1 @@ -exports.unsafeStringify = function (x) { - return JSON.stringify(x); -}; +export const unsafeStringify = (x) => JSON.stringify(x); diff --git a/src/Foreign/JSON.js b/src/Foreign/JSON.js index 5ec76c1..ad36fec 100644 --- a/src/Foreign/JSON.js +++ b/src/Foreign/JSON.js @@ -1,5 +1 @@ -"use strict"; - -exports.parseJSONImpl = function (str) { - return JSON.parse(str); -}; +export const parseJSONImpl = (str) => JSON.parse(str); diff --git a/src/Foreign/NullOrUndefined.js b/src/Foreign/NullOrUndefined.js index 9fd0fcf..0d80ad6 100644 --- a/src/Foreign/NullOrUndefined.js +++ b/src/Foreign/NullOrUndefined.js @@ -1,3 +1,2 @@ -exports['null'] = null; - -exports['undefined'] = undefined; +export const aNull = null; +export const anUndefined = undefined; diff --git a/src/Foreign/NullOrUndefined.purs b/src/Foreign/NullOrUndefined.purs index 507fd0d..50c2918 100644 --- a/src/Foreign/NullOrUndefined.purs +++ b/src/Foreign/NullOrUndefined.purs @@ -10,6 +10,6 @@ readNullOrUndefined :: forall a. (Foreign -> F a) -> Foreign -> F (Maybe a) readNullOrUndefined _ value | isNull value || isUndefined value = pure Nothing readNullOrUndefined f value = Just <$> f value -foreign import undefined :: Foreign +foreign import anUndefined :: Foreign -foreign import null :: Foreign +foreign import aNull :: Foreign From bfebf9e1272ca3de89209b08056765b9daa1cf0e Mon Sep 17 00:00:00 2001 From: jtomas Date: Tue, 29 Mar 2022 07:34:47 +0200 Subject: [PATCH 2/3] poly a --- src/Foreign/Generic/Class.purs | 13 +++++++++++++ test/Main.purs | 2 ++ test/Types.purs | 18 ++++++++++-------- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/Foreign/Generic/Class.purs b/src/Foreign/Generic/Class.purs index 582c851..957da2d 100644 --- a/src/Foreign/Generic/Class.purs +++ b/src/Foreign/Generic/Class.purs @@ -30,6 +30,9 @@ import Record.Builder as Builder import Type.Proxy (Proxy(..)) import Unsafe.Coerce (unsafeCoerce) +newtype Poly a = Poly a +derive newtype instance eqPoly :: Eq a => Eq (Poly a) + -- | Encoding/Decoding options which can be used to customize -- | `Decode` and `Encode` instances which are derived via -- | `Generic` (see `genericEncode` and `genericDecode`). @@ -132,6 +135,9 @@ instance objectDecode :: Decode v => Decode (Object v) where instance recordDecode :: (RowToList r rl, DecodeRecord r rl) => Decode (Record r) where decode = decodeWithOptions defaultOptions +instance polyDecode :: Decode a => Decode (Poly a) where + decode = map Poly <<< decode + -- | The `Encode` class is used to generate encoding functions -- | of the form `a -> Foreign` using `generics-rep` deriving. -- | @@ -189,6 +195,9 @@ instance objectEncode :: Encode v => Encode (Object v) where instance recordEncode :: (RowToList r rl, EncodeRecord r rl) => Encode (Record r) where encode = encodeWithOptions defaultOptions +instance polyEncode :: Encode a => Encode (Poly a) where + encode (Poly a) = encode a + -- | When deriving `En`/`Decode` instances using `Generic`, we want -- | the `Options` object to apply to the outermost record type(s) -- | under the data constructors. @@ -207,11 +216,15 @@ class EncodeWithOptions a where instance decodeWithOptionsRecord :: (RowToList r rl, DecodeRecord r rl) => DecodeWithOptions (Record r) where decodeWithOptions opts = map (flip Builder.build {}) <$> decodeRecordWithOptions (Proxy :: Proxy rl) opts +else instance decodeWithOptionsPoly :: Decode a => DecodeWithOptions (Poly a) where + decodeWithOptions _ = decode else instance decodeWithOptionsOther :: Decode a => DecodeWithOptions a where decodeWithOptions _ = decode instance encodeWithOptionsRecord :: (RowToList r rl, EncodeRecord r rl) => EncodeWithOptions (Record r) where encodeWithOptions opts = unsafeToForeign <<< encodeRecordWithOptions (Proxy :: Proxy rl) opts +else instance encodeWithOptionsPoly :: Encode a => EncodeWithOptions (Poly a) where + encodeWithOptions _ = encode else instance encodeWithOptionsOther :: Encode a => EncodeWithOptions a where encodeWithOptions _ = encode diff --git a/test/Main.purs b/test/Main.purs index 0dc683e..eb9ffda 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -13,6 +13,7 @@ import Effect (Effect) import Effect.Console (log) import Foreign (isNull, unsafeToForeign) import Foreign.Generic (class Decode, class Encode, class GenericDecode, class GenericEncode, Options, decode, encode, defaultOptions, decodeJSON, encodeJSON, genericDecodeJSON, genericEncodeJSON) +import Foreign.Generic.Class (Poly(..)) import Foreign.Generic.EnumEncoding (class GenericDecodeEnum, class GenericEncodeEnum, GenericEnumOptions, genericDecodeEnum, genericEncodeEnum) import Foreign.Index (readProp) import Foreign.Internal.Stringify (unsafeStringify) @@ -131,6 +132,7 @@ main = do testRoundTrip [Just "test"] testRoundTrip [Nothing :: Maybe String] testRoundTrip (Apple) + testRoundTrip (Poly {foo : "foo"}) testRoundTrip (makeTree 0) testRoundTrip (makeTree 5) testRoundTrip (Object.fromFoldable [Tuple "one" 1, Tuple "two" 2]) diff --git a/test/Types.purs b/test/Types.purs index ad2d060..b4e3936 100644 --- a/test/Types.purs +++ b/test/Types.purs @@ -3,14 +3,16 @@ module Test.Types where import Prelude import Data.Bifunctor (class Bifunctor) -import Foreign (ForeignError(..), fail, readArray, unsafeToForeign) -import Foreign.Generic (class Encode, class Decode, Options, SumEncoding(..), encode, decode, defaultOptions, genericDecode, genericEncode) -import Foreign.Generic.EnumEncoding (defaultGenericEnumOptions, genericDecodeEnum, genericEncodeEnum) -import Data.Generic.Rep (class Generic) import Data.Eq.Generic (genericEq) -import Data.Show.Generic (genericShow) +import Data.Generic.Rep (class Generic) import Data.Maybe (Maybe) +import Data.Show.Generic (genericShow) import Data.Tuple (Tuple(..)) +import Foreign (ForeignError(..), fail, readArray, unsafeToForeign) +import Foreign.Generic (class Encode, class Decode, Options, SumEncoding(..), encode, decode, defaultOptions, genericDecode, genericEncode) +import Foreign.Generic.Class (Poly(..)) +import Foreign.Generic.EnumEncoding (defaultGenericEnumOptions, genericDecodeEnum, genericEncodeEnum) +import Safe.Coerce (coerce) newtype TupleArray a b = TupleArray (Tuple a b) @@ -95,10 +97,10 @@ instance eqTree :: Eq a => Eq (Tree a) where eq x y = genericEq x y instance decodeTree :: Decode a => Decode (Tree a) where - decode x = genericDecode defaultOptions x + decode x = (coerce :: Tree (Poly a) -> Tree a) <$> genericDecode defaultOptions x -instance encodeTree :: Encode a => Encode (Tree a) where - encode x = genericEncode defaultOptions x +instance Encode a => Encode (Tree a) where + encode = genericEncode defaultOptions <<< (coerce :: Tree a -> Tree (Poly a)) newtype UndefinedTest = UndefinedTest { a :: Maybe String From b96f644502269ec27e446014c1fca4d73a86889e Mon Sep 17 00:00:00 2001 From: jtomas Date: Tue, 29 Mar 2022 07:40:41 +0200 Subject: [PATCH 3/3] fix (Maximum call stack size exceeded) --- test/Types.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Types.purs b/test/Types.purs index b4e3936..9ff2257 100644 --- a/test/Types.purs +++ b/test/Types.purs @@ -100,7 +100,7 @@ instance decodeTree :: Decode a => Decode (Tree a) where decode x = (coerce :: Tree (Poly a) -> Tree a) <$> genericDecode defaultOptions x instance Encode a => Encode (Tree a) where - encode = genericEncode defaultOptions <<< (coerce :: Tree a -> Tree (Poly a)) + encode x = genericEncode defaultOptions ((coerce :: Tree a -> Tree (Poly a)) x) newtype UndefinedTest = UndefinedTest { a :: Maybe String