Skip to content

Instantly share code, notes, and snippets.

@dgendill
Created March 4, 2017 21:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dgendill/25bbf4cf8a13c7489eefeaba2f10c82a to your computer and use it in GitHub Desktop.
Save dgendill/25bbf4cf8a13c7489eefeaba2f10c82a to your computer and use it in GitHub Desktop.
Using PureScript's IsForeign class to parse a Point type from a String

Using PureScript's IsForeign class to parse a Point type from a String

PureScript's IsForeign typeclass is used to take any foreign value (a value whose type is unknown by the compiler) and turn it into a type the compiler recognizes. This usually happens when you're interacting with native javascript or an API.

module Main where
import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log, logShow)
import Control.Monad.Except (runExcept, throwError)
import Data.Array (head, last, length)
import Data.Either (Either(Right, Left))
import Data.Foldable (traverse_)
import Data.Foreign (F, ForeignError(..), readString, toForeign)
import Data.Foreign.Class (class IsForeign, read)
import Data.Generic (class Generic, gEq, gShow)
import Data.Int (fromString)
import Data.List.NonEmpty (singleton)
import Data.Maybe (Maybe(..))
import Data.String (Pattern(..), split)
-- Create a newtype wrapper for a Point and derive
-- Generic, Eq and Show instances for it...
newtype Point = Point { x :: Int, y :: Int }
derive instance gPoint :: Generic Point
instance eqPoint :: Eq Point where eq = gEq
instance showPoint :: Show Point where show = gShow
-- IsForeign class defines the read function that
-- takes a foreign value (a value that we don't know
-- what it is), and attempts to return the type that we
-- want - in this case a Point
instance fPoint :: IsForeign Point where
read value = do
s <- readString value
let parts = split (Pattern ",") s
if (length parts) == 2
then do
let
mp = do
x <- (head parts >>= fromString)
y <- (last parts >>= fromString)
pure $ Point {x : x, y : y}
case mp of
Nothing -> throwError $ singleton (ForeignError "Could not parse Point from string.")
Just p -> pure p
else throwError $ singleton (ForeignError "Could not parse Point from string.")
runParse :: String -> Eff (console :: CONSOLE) Unit
runParse value = do
log $ "Parsing " <> value
-- Since read can parse any foreign value into
-- any type, we have to explicitly specify the
-- type we intend to read.
let a = runExcept $ read (toForeign value) :: F Point
case a of
Left e -> traverse_ logShow e
Right r -> log $ "Successful Parse: " <> (show a)
main :: Eff (console :: CONSOLE) Unit
main = do
runParse "11"
runParse "10,10"
runParse "a,b"
runParse "6,6,c"
Parsing 11
(ForeignError Could not parse Point from string.)
Parsing 10,10
Successful Parse: (Right Main.Point {x: 10, y: 10})
Parsing a,b
(ForeignError Could not parse Point from string.)
Parsing 6,6,c
(ForeignError Could not parse Point from string.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment