diff --git a/src/Foreign.purs b/src/Foreign.purs index f86d3e4..0873909 100644 --- a/src/Foreign.purs +++ b/src/Foreign.purs @@ -6,6 +6,7 @@ module Foreign , ForeignError(..) , MultipleErrors(..) , F + , FT , renderForeignError , unsafeToForeign , unsafeFromForeign @@ -29,7 +30,7 @@ module Foreign import Prelude -import Control.Monad.Except (Except, throwError, mapExcept) +import Control.Monad.Except (Except, ExceptT, mapExceptT, throwError) import Data.Either (Either(..), either) import Data.Int as Int import Data.List.NonEmpty (NonEmptyList) @@ -81,6 +82,7 @@ renderForeignError (TypeMismatch exp act) = "Type mismatch: expected " <> exp <> -- | The `Alt` instance for `Except` allows us to accumulate errors, -- | unlike `Either`, which preserves only the last error. type F = Except MultipleErrors +type FT = ExceptT MultipleErrors -- | Coerce any value to the a `Foreign` value. -- | @@ -105,7 +107,7 @@ foreign import tagOf :: Foreign -> String -- | Unsafely coerce a `Foreign` value when the value has a particular `tagOf` -- | value. -unsafeReadTagged :: forall a. String -> Foreign -> F a +unsafeReadTagged :: forall m a. Monad m => String -> Foreign -> FT m a unsafeReadTagged tag value | tagOf value == tag = pure (unsafeFromForeign value) | otherwise = fail $ TypeMismatch tag (tagOf value) @@ -120,52 +122,52 @@ foreign import isUndefined :: Foreign -> Boolean foreign import isArray :: Foreign -> Boolean -- | Attempt to coerce a foreign value to a `String`. -readString :: Foreign -> F String +readString :: forall m. Monad m => Foreign -> FT m String readString = unsafeReadTagged "String" -- | Attempt to coerce a foreign value to a `Char`. -readChar :: Foreign -> F Char -readChar value = mapExcept (either (const error) fromString) (readString value) +readChar :: forall m. Monad m => Foreign -> FT m Char +readChar value = mapExceptT (map $ either (const error) fromString) (readString value) where fromString = maybe error pure <<< toChar error = Left $ NEL.singleton $ TypeMismatch "Char" (tagOf value) -- | Attempt to coerce a foreign value to a `Boolean`. -readBoolean :: Foreign -> F Boolean +readBoolean :: forall m. Monad m => Foreign -> FT m Boolean readBoolean = unsafeReadTagged "Boolean" -- | Attempt to coerce a foreign value to a `Number`. -readNumber :: Foreign -> F Number +readNumber :: forall m. Monad m => Foreign -> FT m Number readNumber = unsafeReadTagged "Number" -- | Attempt to coerce a foreign value to an `Int`. -readInt :: Foreign -> F Int -readInt value = mapExcept (either (const error) fromNumber) (readNumber value) +readInt :: forall m. Monad m => Foreign -> FT m Int +readInt value = mapExceptT (map $ either (const error) fromNumber) (readNumber value) where fromNumber = maybe error pure <<< Int.fromNumber error = Left $ NEL.singleton $ TypeMismatch "Int" (tagOf value) -- | Attempt to coerce a foreign value to an array. -readArray :: Foreign -> F (Array Foreign) +readArray :: forall m. Monad m => Foreign -> FT m (Array Foreign) readArray value | isArray value = pure $ unsafeFromForeign value | otherwise = fail $ TypeMismatch "array" (tagOf value) -readNull :: Foreign -> F (Maybe Foreign) +readNull :: forall m. Monad m => Foreign -> FT m (Maybe Foreign) readNull value | isNull value = pure Nothing | otherwise = pure (Just value) -readUndefined :: Foreign -> F (Maybe Foreign) +readUndefined :: forall m. Monad m => Foreign -> FT m (Maybe Foreign) readUndefined value | isUndefined value = pure Nothing | otherwise = pure (Just value) -readNullOrUndefined :: Foreign -> F (Maybe Foreign) +readNullOrUndefined :: forall m. Monad m => Foreign -> FT m (Maybe Foreign) readNullOrUndefined value | isNull value || isUndefined value = pure Nothing | otherwise = pure (Just value) --- | Throws a failure error in `F`. -fail :: forall a. ForeignError -> F a +-- | Throws a failure error in `FT`. +fail :: forall m a. Monad m => ForeignError -> FT m a fail = throwError <<< NEL.singleton diff --git a/src/Foreign/Index.purs b/src/Foreign/Index.purs index 405e72f..daff17b 100644 --- a/src/Foreign/Index.purs +++ b/src/Foreign/Index.purs @@ -17,37 +17,36 @@ import Prelude import Control.Monad.Except.Trans (ExceptT) -import Foreign (Foreign, F, ForeignError(..), typeOf, isUndefined, isNull, fail) +import Foreign (Foreign, FT, ForeignError(..), typeOf, isUndefined, isNull, fail) import Data.Function.Uncurried (Fn2, runFn2, Fn4, runFn4) -import Data.Identity (Identity) import Data.List.NonEmpty (NonEmptyList) -- | This type class identifies types that act like _property indices_. -- | -- | The canonical instances are for `String`s and `Int`s. -class Index i where - index :: Foreign -> i -> F Foreign +class Index i m | i -> m where + index :: Foreign -> i -> FT m Foreign hasProperty :: i -> Foreign -> Boolean hasOwnProperty :: i -> Foreign -> Boolean errorAt :: i -> ForeignError -> ForeignError -class Indexable a where - ix :: forall i. Index i => a -> i -> F Foreign +class Indexable a m | a -> m where + ix :: forall i. Index i m => a -> i -> FT m Foreign infixl 9 ix as ! foreign import unsafeReadPropImpl :: forall r k. Fn4 r (Foreign -> r) k Foreign r -unsafeReadProp :: forall k. k -> Foreign -> F Foreign +unsafeReadProp :: forall k m. Monad m => k -> Foreign -> FT m Foreign unsafeReadProp k value = runFn4 unsafeReadPropImpl (fail (TypeMismatch "object" (typeOf value))) pure k value -- | Attempt to read a value from a foreign value property -readProp :: String -> Foreign -> F Foreign +readProp :: forall m. Monad m => String -> Foreign -> FT m Foreign readProp = unsafeReadProp -- | Attempt to read a value from a foreign value at the specified numeric index -readIndex :: Int -> Foreign -> F Foreign +readIndex :: forall m. Monad m => Int -> Foreign -> FT m Foreign readIndex = unsafeReadProp foreign import unsafeHasOwnProperty :: forall k. Fn2 k Foreign Boolean @@ -66,20 +65,20 @@ hasPropertyImpl _ value | isUndefined value = false hasPropertyImpl p value | typeOf value == "object" || typeOf value == "function" = runFn2 unsafeHasProperty p value hasPropertyImpl _ value = false -instance indexString :: Index String where +instance indexString :: Monad m => Index String m where index = flip readProp hasProperty = hasPropertyImpl hasOwnProperty = hasOwnPropertyImpl errorAt = ErrorAtProperty -instance indexInt :: Index Int where +instance indexInt :: Monad m => Index Int m where index = flip readIndex hasProperty = hasPropertyImpl hasOwnProperty = hasOwnPropertyImpl errorAt = ErrorAtIndex -instance indexableForeign :: Indexable Foreign where +instance indexableForeign :: Monad m => Indexable Foreign m where ix = index -instance indexableExceptT :: Indexable (ExceptT (NonEmptyList ForeignError) Identity Foreign) where +instance indexableExceptT :: Monad m => Indexable (ExceptT (NonEmptyList ForeignError) m Foreign) m where ix f i = flip index i =<< f diff --git a/src/Foreign/Keys.purs b/src/Foreign/Keys.purs index c600be1..546c4d6 100644 --- a/src/Foreign/Keys.purs +++ b/src/Foreign/Keys.purs @@ -7,12 +7,12 @@ module Foreign.Keys import Prelude -import Foreign (F, Foreign, ForeignError(..), typeOf, isUndefined, isNull, fail) +import Foreign (FT, Foreign, ForeignError(..), typeOf, isUndefined, isNull, fail) foreign import unsafeKeys :: Foreign -> Array String -- | Get an array of the properties defined on a foreign value -keys :: Foreign -> F (Array String) +keys :: forall m. Monad m => Foreign -> FT m (Array String) keys value | isNull value = fail $ TypeMismatch "object" "null" | isUndefined value = fail $ TypeMismatch "object" "undefined"