Skip to content

Commit dc05252

Browse files
authored
Merge pull request #42 from puffnfresh/feature/asforeign
Add a class to create Foreign types
2 parents bdad492 + c362732 commit dc05252

File tree

10 files changed

+102
-14
lines changed

10 files changed

+102
-14
lines changed

examples/JSONSimpleTypes.purs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ module Example.JSONSimpleTypes where
33
import Prelude
44

55
import Control.Monad.Eff (Eff)
6-
import Control.Monad.Eff.Console (CONSOLE, logShow)
6+
import Control.Monad.Eff.Console (CONSOLE, log, logShow)
77

8-
import Data.Foreign (F)
9-
import Data.Foreign.Class (readJSON)
8+
import Data.Foreign (F, unsafeFromForeign)
9+
import Data.Foreign.Class (readJSON, write)
1010

1111
-- Parsing of the simple JSON String, Number and Boolean types is provided
1212
-- out of the box.
@@ -15,3 +15,6 @@ main = do
1515
logShow $ readJSON "\"a JSON string\"" :: F String
1616
logShow $ readJSON "42" :: F Number
1717
logShow $ readJSON "true" :: F Boolean
18+
log $ unsafeFromForeign $ write "a JSON string"
19+
log $ unsafeFromForeign $ write 42.0
20+
log $ unsafeFromForeign $ write true

examples/MaybeNullable.purs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@ module Example.MaybeNullable where
33
import Prelude
44

55
import Control.Monad.Eff (Eff)
6-
import Control.Monad.Eff.Console (CONSOLE, logShow)
6+
import Control.Monad.Eff.Console (CONSOLE, log, logShow)
77

8-
import Data.Foreign (F)
9-
import Data.Foreign.Class (readJSON)
10-
import Data.Foreign.Null (Null, unNull)
8+
import Data.Foreign (F, unsafeFromForeign)
9+
import Data.Foreign.Class (readJSON, write)
10+
import Data.Foreign.Null (Null(..), unNull)
11+
import Data.Maybe (Maybe(..))
1112

1213
-- Parsing values that are allowed to null or undefined is possible by
1314
-- using Maybe types.
1415
main :: Eff (console :: CONSOLE) Unit
1516
main = do
1617
logShow $ unNull <$> readJSON "null" :: F (Null Boolean)
1718
logShow $ unNull <$> readJSON "true" :: F (Null Boolean)
19+
log $ unsafeFromForeign $ write $ Null Nothing :: Null Boolean
20+
log $ unsafeFromForeign $ write $ Null $ Just true

examples/Objects.purs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ module Example.Objects where
33
import Prelude
44

55
import Control.Monad.Eff (Eff)
6-
import Control.Monad.Eff.Console (CONSOLE, logShow)
6+
import Control.Monad.Eff.Console (CONSOLE, log, logShow)
77

8-
import Data.Foreign (F)
9-
import Data.Foreign.Class (class IsForeign, readJSON, readProp)
8+
import Data.Foreign (F, writeObject, unsafeFromForeign)
9+
import Data.Foreign.Class (class AsForeign, class IsForeign, (.=), readJSON, readProp, write)
1010

1111
-- | To parse objects of a particular type, we need to define some helper
1212
-- data types as making class instances for records is not possible.
@@ -15,6 +15,11 @@ data Point = Point { x :: Number, y :: Number }
1515
instance showPoint :: Show Point where
1616
show (Point o) = "(Point { x: " <> show o.x <> ", y: " <> show o.y <> " })"
1717

18+
instance pointAsForeign :: AsForeign Point where
19+
write (Point o) = writeObject [ "x" .= o.x
20+
, "y" .= o.y
21+
]
22+
1823
-- | The IsForeign implementations for these types are basically boilerplate,
1924
-- type inference takes care of most of the work so we don't have to
2025
-- explicitly define the type each of the properties we're parsing.
@@ -27,3 +32,4 @@ instance pointIsForeign :: IsForeign Point where
2732
main :: Eff (console :: CONSOLE) Unit
2833
main = do
2934
logShow $ readJSON """{ "x": 1, "y": 2 }""" :: F Point
35+
log $ unsafeFromForeign $ write $ Point { x: 1.0, y: 2.0 }

src/Data/Foreign.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,11 @@ exports.isUndefined = function (value) {
4040
exports.isArray = Array.isArray || function (value) {
4141
return Object.prototype.toString.call(value) === "[object Array]";
4242
};
43+
44+
exports.writeObject = function (fields) {
45+
var record = {};
46+
for (var i = 0; i < fields.length; i++) {
47+
record[fields[i].key] = fields[i].value;
48+
}
49+
return record;
50+
};

src/Data/Foreign.purs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
module Data.Foreign
55
( Foreign()
66
, ForeignError(..)
7+
, Prop(..)
78
, F()
89
, parseJSON
910
, toForeign
@@ -20,6 +21,7 @@ module Data.Foreign
2021
, readNumber
2122
, readInt
2223
, readArray
24+
, writeObject
2325
) where
2426

2527
import Prelude
@@ -137,3 +139,7 @@ readInt value = either (const error) fromNumber (readNumber value)
137139
readArray :: Foreign -> F (Array Foreign)
138140
readArray value | isArray value = pure $ unsafeFromForeign value
139141
readArray value = Left (TypeMismatch "array" (tagOf value))
142+
143+
newtype Prop = Prop { key :: String, value :: Foreign }
144+
145+
foreign import writeObject :: Array Prop -> Foreign

src/Data/Foreign/Class.purs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,27 @@
22

33
module Data.Foreign.Class
44
( class IsForeign
5+
, class AsForeign
6+
, (.=)
57
, read
68
, readJSON
79
, readWith
810
, readProp
11+
, write
12+
, writeProp
913
) where
1014

1115
import Prelude
1216

1317
import Data.Array (range, zipWith, length)
1418
import Data.Either (Either(..), either)
15-
import Data.Foreign (F, Foreign, ForeignError(..), parseJSON, readArray, readInt, readNumber, readBoolean, readChar, readString)
19+
import Data.Foreign (F, Foreign, ForeignError(..), Prop(..), parseJSON, readArray, readInt, readNumber, readBoolean, readChar, readString, toForeign)
1620
import Data.Foreign.Index (class Index, errorAt, (!))
17-
import Data.Foreign.Null (Null, readNull)
18-
import Data.Foreign.NullOrUndefined (NullOrUndefined, readNullOrUndefined)
19-
import Data.Foreign.Undefined (Undefined, readUndefined)
21+
import Data.Foreign.Null (Null(..), readNull, writeNull)
22+
import Data.Foreign.NullOrUndefined (NullOrUndefined(..), readNullOrUndefined)
23+
import Data.Foreign.Undefined (Undefined(..), readUndefined, writeUndefined)
2024
import Data.Traversable (sequence)
25+
import Data.Maybe (maybe)
2126

2227
-- | A type class instance for this class can be written for a type if it
2328
-- | is possible to attempt to _safely_ coerce a `Foreign` value to that
@@ -75,3 +80,44 @@ readWith f value = either (Left <<< f) Right (read value)
7580
-- | Attempt to read a property of a foreign value at the specified index
7681
readProp :: forall a i. (IsForeign a, Index i) => i -> Foreign -> F a
7782
readProp prop value = value ! prop >>= readWith (errorAt prop)
83+
84+
-- | A type class to convert to a `Foreign` value.
85+
-- |
86+
-- | Instances are provided for standard data structures.
87+
class AsForeign a where
88+
write :: a -> Foreign
89+
90+
instance foreignAsForeign :: AsForeign Foreign where
91+
write = id
92+
93+
instance stringAsForeign :: AsForeign String where
94+
write = toForeign
95+
96+
instance charAsForeign :: AsForeign Char where
97+
write = toForeign
98+
99+
instance booleanAsForeign :: AsForeign Boolean where
100+
write = toForeign
101+
102+
instance numberAsForeign :: AsForeign Number where
103+
write = toForeign
104+
105+
instance intAsForeign :: AsForeign Int where
106+
write = toForeign
107+
108+
instance arrayAsForeign :: (AsForeign a) => AsForeign (Array a) where
109+
write = toForeign <<< map write
110+
111+
instance nullAsForeign :: (AsForeign a) => AsForeign (Null a) where
112+
write (Null a) = maybe writeNull write a
113+
114+
instance undefinedAsForeign :: (AsForeign a) => AsForeign (Undefined a) where
115+
write (Undefined a) = maybe writeUndefined write a
116+
117+
instance nullOrUndefinedAsForeign :: (AsForeign a) => AsForeign (NullOrUndefined a) where
118+
write (NullOrUndefined a) = write (Null a)
119+
120+
infixl 8 writeProp as .=
121+
122+
writeProp :: forall a. (AsForeign a) => String -> a -> Prop
123+
writeProp k v = Prop { key: k, value: write v }

src/Data/Foreign/Null.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/* global exports */
2+
"use strict";
3+
4+
// module Data.Foreign.Null
5+
6+
exports.writeNull = null;

src/Data/Foreign/Null.purs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,5 @@ unNull (Null m) = m
2020
readNull :: forall a. (Foreign -> F a) -> Foreign -> F (Null a)
2121
readNull _ value | isNull value = pure (Null Nothing)
2222
readNull f value = Null <<< Just <$> f value
23+
24+
foreign import writeNull :: Foreign

src/Data/Foreign/Undefined.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/* global exports */
2+
"use strict";
3+
4+
// module Data.Foreign.Undefined
5+
6+
exports.writeUndefined = undefined;

src/Data/Foreign/Undefined.purs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,5 @@ unUndefined (Undefined m) = m
2020
readUndefined :: forall a. (Foreign -> F a) -> Foreign -> F (Undefined a)
2121
readUndefined _ value | isUndefined value = pure (Undefined Nothing)
2222
readUndefined f value = Undefined <<< Just <$> f value
23+
24+
foreign import writeUndefined :: Foreign

0 commit comments

Comments
 (0)