prefer nested where to let

prefer nested where to let

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/michaelsbradleyjr/advent-of-code.git/'


solution for Day2 Part2

solution for Day2 Part2


Type declarations for the parts of jQuery UI that I need

module JQueryUI where

import Control.Monad.Eff
import Control.Monad.JQuery(DOM(..), JQuery(..))

-- .accordion(config)
foreign import accordion
  "function(config) { \
  \  return function(ob) { \
  \    return function() { \
  \      ob.accordion(config); \
  \    }; \
  \  }; \
  \}" :: forall eff r. { | r } -> JQuery -> Eff (dom :: DOM | eff) JQuery

-- .button()
foreign import button
  "function(ob) { \
  \  return function() { \
  \    ob.button(); \
  \  }; \
  \}" :: forall eff. JQuery -> Eff (dom :: DOM | eff) JQuery

-- .buttonset()
foreign import buttonset
  "function(ob) { \
  \  return function() { \
  \    ob.buttonset(); \
  \  }; \
  \}" :: forall eff. JQuery -> Eff (dom :: DOM | eff) JQuery

-- .change()
foreign import change
  "function(ob) { \
  \  return function() { \
  \    ob.change(); \
  \  }; \
  \}" :: forall eff. JQuery -> Eff (dom :: DOM | eff) JQuery

-- .click(f)
foreign import click
  "function(f) { \
  \  return function(ob) { \
  \    return function() { \
  \      ob.click(f); \
  \    }; \
  \  }; \
  \}" :: forall eff a. (Unit -> Eff eff a) -> JQuery -> Eff (dom :: DOM | eff) JQuery

-- .each(f)
foreign import each
  "function(f) { \
  \  return function(ob) { \
  \    return function() { \
  \      ob.each(f); \
  \    }; \
  \  }; \
  \}" :: forall eff a. (Unit -> Eff eff a) -> JQuery -> Eff (dom :: DOM | eff) JQuery

-- x[0].checked = !x[0].checked
foreign import flipChecked
  "function(ob) { \
  \  return function () { \
  \    ob[0].checked = !ob[0].checked; \
  \  }; \
  \}" :: forall eff. JQuery -> Eff (dom :: DOM | eff) Unit

-- .button('refresh')
foreign import refreshButton
  "function(ob) { \
  \  return function() { \
  \    ob.button('refresh'); \
  \  }; \
  \}" :: forall eff. JQuery -> Eff (dom :: DOM | eff) Unit

-- .siblings(filterStr)
foreign import siblings
  "function(filterStr) { \
  \  return function(ob) { \
  \    return function() { \
  \      ob.siblings(filterStr); \
  \    }; \
  \  }; \
  \}" :: forall eff. String -> JQuery -> Eff (dom :: DOM | eff) JQuery

-- .spinner()
foreign import spinner
  "function(ob) { \
  \  return function() { \
  \    ob.spinner(); \
  \  }; \
  \}" :: forall eff. JQuery -> Eff (dom :: DOM | eff) JQuery

-- .tabs()
foreign import tabs
  "function(ob) { \
  \  return function() { \
  \    ob.tabs(); \
  \  }; \
  \}" :: forall eff. JQuery -> Eff (dom :: DOM | eff) JQuery

-- .val(newVal)
foreign import val
  "function(newVal) { \
  \  return function(ob) { \
  \    return function() { \
  \      ob.val(newVal); \
  \    }; \
  \  }; \
  \}" :: forall eff. String -> JQuery -> Eff (dom :: DOM | eff) JQuery

error: pathspec 'src/client/js/JQueryUI.purs' did not match any file(s) known to git


Language.PureScript.Optimizer.Unused

Language.PureScript.Optimizer.Unused

module Language.PureScript.Optimizer.Unused
  ( removeUnusedVariables
  , removeCodeAfterReturnStatements
  ) where

import Data.Array (span)
import Data.Foldable (any)
import Data.Tuple
import Language.PureScript.CodeGen.JS.AST
import Language.PureScript.Optimizer.Common

removeUnusedVariables :: JS -> JS
removeUnusedVariables = everywhereOnJS (removeFromBlock withBlock)
  where
  withBlock :: [JS] -> [JS]
  withBlock sts = go sts sts
  go :: [JS] -> [JS] -> [JS]
  go _ [] = []
  go sts (JSVariableIntroduction var _ : rest) | not (any (isUsed var) sts) = go sts rest
  go sts (s : rest) = s : go sts rest

removeCodeAfterReturnStatements :: JS -> JS
removeCodeAfterReturnStatements = everywhereOnJS (removeFromBlock go)
  where
  go :: [JS] -> [JS]
  go jss | not (any isJSReturn jss) = jss
  go jss = case span (not <<< isJSReturn) jss of
    { init = body, rest = (ret : _) } -> body ++ [ret]
  isJSReturn (JSReturn _) = true
  isJSReturn _ = false

error: pathspec 'src/Language/PureScript/Optimizer/Unused.purs' did not match any file(s) known to git


Change column spacing in help text, closes #48

-- | This module defines the interpreter for Insect.
module Insect.Interpreter
  ( MessageType(..)
  , Message(..)
  , runInsect
  ) where

import Prelude hiding (degree)

import Data.Bifunctor (lmap)
import Data.Either (Either(..))
import Data.Foldable (intercalate)
import Data.Maybe (Maybe(..))
import Data.StrMap (lookup, insert, foldMap)

import Quantities (Quantity, UnificationError, pow, scalar, qNegate, qAdd,
                   qDivide, qMultiply, qSubtract, quantity, toScalar', sqrt,
                   convertTo, errorMessage, prettyPrint, fullSimplify,
                   derivedUnit, acos, asin, atan, sin, cos, tan, exp, ln,
                   sinh, cosh, tanh, asinh, acosh, atanh, ceil, floor, log10,
                   round)

import Insect.Language (Func(..), BinOp(..), Expression(..), Command(..), Statement(..))
import Insect.Environment (Environment, initialEnvironment)

-- | Types of errors that may appear during evaluation.
data EvalError
  = UnificationError UnificationError
  | LookupError String

-- | A type synonym for error handling. A value of type `Expect Number` is
-- | expected to be a number but might also result in an evaluation error.
type Expect = Either EvalError

-- | Output types for highlighting.
data MessageType = Value | ValueSet | Info | Error | Other

-- | The output type of the interpreter.
data Message = Message MessageType String

-- | Apply a mathematical function to a physical quantity.
applyFunction ∷ Func → Quantity → Expect Quantity
applyFunction fn q = lmap UnificationError $ (run fn) q
  where
    run Acos  = acos
    run Acosh = acosh
    run Asin  = asin
    run Asinh = asinh
    run Atan  = atan
    run Atanh = atanh
    run Ceil  = ceil
    run Cos   = cos
    run Cosh  = cosh
    run Exp   = exp
    run Floor = floor
    run Ln    = ln
    run Log10 = log10
    run Round = round
    run Sin   = sin
    run Sinh  = sinh
    run Sqrt  = sqrt >>> pure
    run Tan   = tan
    run Tanh  = tanh

-- | Evaluate a mathematical expression involving physical quantities.
eval ∷ Environment → Expression → Expect Quantity
eval env (Scalar n)      = pure $ scalar n
eval env (Unit u)        = pure $ quantity 1.0 u
eval env (Variable name) =
  case lookup name env of
    Just q → pure q
    Nothing → Left (LookupError name)
eval env (Negate x)      = qNegate <$> eval env x
eval env (Apply fn x)    = eval env x >>= applyFunction fn
eval env (BinOp op x y)  = do
  x' <- eval env x
  y' <- eval env y
  (run op) x' y'
  where
    run :: BinOp -> Quantity -> Quantity -> Expect Quantity
    run Sub       a b = qSubtract' a b
    run Add       a b = qAdd' a b
    run Mul       a b = pure (qMultiply a b)
    run Div       a b = pure (qDivide a b)
    run Pow       a b = pow a <$> toScalar'' b
    run ConvertTo a b = convertTo' a b

    wrap ∷ ∀ a. Either UnificationError a → Either EvalError a
    wrap = lmap UnificationError

    qSubtract' q1 q2 = wrap (qSubtract q1 q2)
    qAdd' q1 q2 = wrap (qAdd q1 q2)
    toScalar'' q = wrap (toScalar' q)
    convertTo' source target = wrap (convertTo source (derivedUnit target))

-- | Get the error message for an evaluation error.
evalErrorMessage ∷ EvalError → String
evalErrorMessage (UnificationError ue) = errorMessage ue
evalErrorMessage (LookupError name) = "Unknown variable '" <> name <> "'"

-- | Interpreter return type
type Response = { msg ∷ Message, newEnv ∷ Environment }

-- | Helper to construct an interpreter response
message ∷ MessageType → Environment → Expect Quantity → Response
message _ env (Left e) =
  { msg: Message Error (evalErrorMessage e)
  , newEnv: env
  }
message mt env (Right q) =
  { msg: Message mt (prettyPrint q)
  , newEnv: insert "ans" q env
  }

-- | Run a single statement of an Insect program.
runInsect ∷ Environment → Statement → Response
runInsect env (Expression e) = message Value env (fullSimplify <$> eval env e)
runInsect env (Assignment n v) =
  case eval env v of
    Left evalErr → message Error env (Left evalErr)
    Right value → message ValueSet (insert n value env) (Right (fullSimplify value))
runInsect env (Command Help) = { msg: Message Other (intercalate "\n"
  [ ""
  , "*insect* evaluates mathematical expressions that can"
  , "involve physical quantities. You can start by trying"
  , "one of these examples:"
  , ""
  , "  > `1920/16*9`               > `sin(30deg)`"
  , ""
  , "  > `2min + 30s`              > `6Mbit/s * 1.5h -> Gb`"
  , ""
  , "  > `list`                    > `r = 80cm`"
  , "  > `40000km/c -> ms`         > `pi*r^2 -> m^2`"
  , ""
  , "More information: https://github.com/sharkdp/insect"
  ]), newEnv : env }
runInsect env (Command List) =
  { msg: Message Other list
  , newEnv: env }
  where
    list = "List of variables:\n" <> foldMap toLine env
    toLine k v = "\n  * " <> k <> " = `" <> prettyPrint v <> "`"
runInsect env (Command Reset) =
  { msg: Message Info "Environment has been reset"
  , newEnv: initialEnvironment }
runInsect env (Command _) = { msg: Message Error "???", newEnv: env }


Header now created correctly using composition.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/0dB/cats-utils.git/'


Add Address newtype over Int

Add Address newtype over Int

Used to identify nodes in the heap

module Machine.Address
  ( Address
  ) where

import Prelude

import Data.Generic.Rep (class Generic)
import Data.Hashable (class Hashable)
import Data.Newtype (class Newtype)

newtype Address = Address Int

derive instance newtypeAddress :: Newtype Address _
derive instance genericAddress :: Generic Address _
derive newtype instance eqAddress :: Eq Address
derive newtype instance hashableAddress :: Hashable Address
derive newtype instance ordAddress :: Ord Address
derive newtype instance semiringAddress :: Semiring Address
derive newtype instance ringAddress :: Ring Address

instance showAddress :: Show Address where
  show (Address i) = "#" <> show i

error: pathspec 'src/Machine/Address.purs' did not match any file(s) known to git


Some of Language.PureScript.TypeChecker.Types

module Language.PureScript.TypeChecker.Types {-(typesOf)-} where

{-
  The following functions represent the corresponding type checking judgements:

    infer
      Synthesize a type for a value

    check
      Check a value has a given type

    checkProperties
      Check an object with a given type contains specified properties

    checkFunctionApplication
      Check a function of a given type returns a value of another type when applied to its arguments

    subsumes
      Check a type subsumes another type
-}

import Data.Array (length, map, nub, sort, sortBy)
import Data.Either
import Data.Foldable (foldl)
import Data.Function (on)
import Data.Maybe
import Data.Monoid
import Data.Tuple
import Data.Tuple3

import Language.PureScript.Declarations
import Language.PureScript.Types
import Language.PureScript.Kinds
import Language.PureScript.Names
import Language.PureScript.TypeClassDictionaries
import Language.PureScript.TypeChecker.Monad
import Language.PureScript.TypeChecker.Kinds
import Language.PureScript.TypeChecker.Synonyms
import Language.PureScript.Pretty.Types
import Language.PureScript.Pretty.Values
import Language.PureScript.Environment
import Language.PureScript.Errors
import qualified Language.PureScript.Constants as C

import Control.Bind ((<=<))
import Control.Monad.State
import Control.Monad.State.Class
import Control.Monad.Error
import Control.Monad.Error.Class
import Control.Monad.Error.Proxy
import Control.Monad.Unify

import Control.Arrow (first)

import qualified Data.Map as M

instance partialType :: Partial Type where
  unknown = TUnknown
  isUnknown (TUnknown u) = Just u
  isUnknown _ = Nothing
  unknowns = everythingOnTypes (++) go
    where
    go (TUnknown u) = [u]
    go _ = []
  ($?) sub = everywhereOnTypes go
    where
    go t@(TUnknown u) = fromMaybe t $ M.lookup u (runSubstitution sub)
    go other = other

{-
instance unifiableCheckType :: Unifiable Check Type where
  (=?=) = unifyTypes

-- |
-- Unify two types, updating the current substitution
--
unifyTypes :: Type -> Type -> UnifyT Type Check {}
unifyTypes t1 t2 = rethrow (\x -> mkErrorStack ("Error unifying type " ++ prettyPrintType t1 ++ " with type " ++ prettyPrintType t2) Nothing <> x) $
  unifyTypes' t1 t2
  where
  unifyTypes' (TUnknown u1) (TUnknown u2) | u1 == u2 = return {}
  unifyTypes' (TUnknown u) t = u =:= t
  unifyTypes' t (TUnknown u) = u =:= t
  unifyTypes' (SaturatedTypeSynonym name args) ty = do
    ty1 <- introduceSkolemScope <=< expandTypeSynonym name $ args
    ty1 `unifyTypes` ty
  unifyTypes' ty s@(SaturatedTypeSynonym _ _) = s `unifyTypes` ty
  unifyTypes' (ForAll ident1 ty1 sc1) (ForAll ident2 ty2 sc2) =
    case Tuple sc1 sc2 of
      Tuple (Just sc1') (Just sc2') -> do
        sko <- newSkolemConstant
        let sk1 = skolemize ident1 sko sc1' ty1
        let sk2 = skolemize ident2 sko sc2' ty2
        sk1 `unifyTypes` sk2
      _ -> error "Skolemized type variable was not given a scope"
  unifyTypes' (ForAll ident ty1 (Just sc)) ty2 = do
    sko <- newSkolemConstant
    let sk = skolemize ident sko sc ty1
    sk `unifyTypes` ty2
  unifyTypes' ForAll{} _ = throwError <<< strMsg $ "Skolem variable scope is unspecified"
  unifyTypes' ty f@ForAll{} = f `unifyTypes` ty
  unifyTypes' (TypeVar v1) (TypeVar v2) | v1 == v2 = return {}
  unifyTypes' (TypeConstructor c1) (TypeConstructor c2) =
    guardWith (strMsg ("Cannot unify " ++ show c1 ++ " with " ++ show c2 ++ ".")) (c1 == c2)
  unifyTypes' (TypeApp t3 t4) (TypeApp t5 t6) = do
    t3 `unifyTypes` t5
    t4 `unifyTypes` t6
  unifyTypes' (Skolem _ s1 _) (Skolem _ s2 _) | s1 == s2 = return {}
  unifyTypes' r1@RCons{} r2 = unifyRows r1 r2
  unifyTypes' r1 r2@RCons{} = unifyRows r1 r2
  unifyTypes' r1@REmpty r2 = unifyRows r1 r2
  unifyTypes' r1 r2@REmpty = unifyRows r1 r2
  unifyTypes' t@(ConstrainedType _ _) _ = throwError <<< strMsg $ "Attempted to unify a constrained type " ++ prettyPrintType t ++ " with another type."
  unifyTypes' t3 t4@(ConstrainedType _ _) = unifyTypes' t4 t3
  unifyTypes' t3 t4 = throwError <<< strMsg $ "Cannot unify " ++ prettyPrintType t3 ++ " with " ++ prettyPrintType t4 ++ "."

-- |
-- Unify two rows, updating the current substitution
--
-- Common labels are first identified, and unified. Remaining labels and types are unified with a
-- trailing row unification variable, if appropriate, otherwise leftover labels result in a unification
-- error.
--
unifyRows :: Type -> Type -> UnifyT Type Check {}
unifyRows r1 r2 =
  case Tuple (rowToList r1) (rowToList r2) of
    Tuple (Tuple s1 r1') (Tuple s2 r2') ->
      let int = [ (t1, t2) | (name, t1) <- s1, (name', t2) <- s2, name == name' ]
          sd1 = [ (name, t1) | (name, t1) <- s1, name `notElem` map fst s2 ]
          sd2 = [ (name, t2) | (name, t2) <- s2, name `notElem` map fst s1 ]
      in do
        forM_ int (uncurry (=?=))
        unifyRows' sd1 r1' sd2 r2'
  where
  unifyRows' :: [(String, Type)] -> Type -> [(String, Type)] -> Type -> UnifyT Type Check {}
  unifyRows' [] (TUnknown u) sd r = u =:= rowFromList (sd, r)
  unifyRows' sd r [] (TUnknown u) = u =:= rowFromList (sd, r)
  unifyRows' ((name, ty):row) r others u@(TUnknown un) = do
    occursCheck un ty
    forM_ row $ \(_, t) -> occursCheck un t
    u' <- fresh
    u =?= RCons name ty u'
    unifyRows' row r others u'
  unifyRows' [] REmpty [] REmpty = return {}
  unifyRows' [] (TypeVar v1) [] (TypeVar v2) | v1 == v2 = return {}
  unifyRows' [] (Skolem _ s1 _) [] (Skolem _ s2 _) | s1 == s2 = return {}
  unifyRows' sd3 r3 sd4 r4 = throwError <<< strMsg $ "Cannot unify (" ++ prettyPrintRow (rowFromList (sd3, r3)) ++ ") with (" ++ prettyPrintRow (rowFromList (sd4, r4)) ++ ")"

-- |
-- Infer the types of multiple mutually-recursive values, and return elaborated values including
-- type class dictionaries and type annotations.
--
typesOf :: Maybe ModuleName -> ModuleName -> [(Ident, Value)] -> Check [(Ident, (Value, Type))]
typesOf mainModuleName moduleName vals = do
  tys <- fmap tidyUp <<< liftUnify $ do
    (es, dict, untypedDict) <- typeDictionaryForBindingGroup moduleName vals
    forM es $ \e -> do
      triple@(_, (_, ty)) <- typeForBindingGroupElement moduleName e dict untypedDict
      -- If --main is enabled, need to check that `main` has type Eff eff a for some eff, a
      when (Just moduleName == mainModuleName && fst e == Ident C.main) $ do
        [eff, a] <- replicateM 2 fresh
        ty =?= TypeApp (TypeApp (TypeConstructor (Qualified (Just (ModuleName [ProperName "Control", ProperName "Monad", ProperName "Eff"])) (ProperName "Eff"))) eff) a
      return triple

  forM tys $ \(ident, (val, ty)) -> do
    -- Replace type class dictionary placeholders with actual dictionaries
    val' <- replaceTypeClassDictionaries moduleName val
    -- Check skolem variables did not escape their scope
    skolemEscapeCheck val'
    -- Remove type synonyms placeholders, remove duplicate row fields, and replace
    -- top-level unification variables with named type variables.
    let val'' = overTypes (desaturateAllTypeSynonyms <<< setifyAll) val'
        ty' = varIfUnknown <<< desaturateAllTypeSynonyms <<< setifyAll $ ty
    return (ident, (val'', ty'))
  where
  -- Apply the substitution that was returned from runUnify to both types and (type-annotated) values
  tidyUp (ts, sub) = map (\(i, (val, ty)) -> (i, (overTypes (sub $?) val, sub $? ty))) ts

typeDictionaryForBindingGroup :: ModuleName -> [(Ident, Value)] -> UnifyT Type Check ([(Ident, (Value, Maybe (Type, Bool)))], M.Map (ModuleName, Ident) (Type, NameKind), [(Ident, Type)])
typeDictionaryForBindingGroup moduleName vals = do
  let
    -- Map each declaration to a name/value pair, with an optional type, if the declaration is typed
    es = map isTyped vals
    -- Filter the typed and untyped declarations
    typed = filter (isJust <<< snd <<< snd) es
    untyped = filter (isNothing <<< snd <<< snd) es
    -- Make a map of names to typed declarations
    typedDict = map (\(ident, (_, Just (ty, _))) -> (ident, ty)) typed

  -- Create fresh unification variables for the types of untyped declarations
  untypedNames <- replicateM (length untyped) fresh

  let
    -- Make a map of names to the unification variables of untyped declarations
    untypedDict = zip (map fst untyped) untypedNames
    -- Create the dictionary of all name/type pairs, which will be added to the environment during type checking
    dict = M.fromList (map (\(ident, ty) -> ((moduleName, ident), (ty, LocalVariable))) $ typedDict ++ untypedDict)
  return (es, dict, untypedDict)

typeForBindingGroupElement :: ModuleName -> (Ident, (Value, Maybe (Type, Bool))) -> M.Map (ModuleName, Ident) (Type, NameKind) -> [(Ident, Type)] -> UnifyT Type Check (Ident, (Value, Type))
typeForBindingGroupElement moduleName e@(_, (val, _)) dict untypedDict = do
  -- If the declaration is a function, it has access to other values in the binding group.
  -- If not, the generated code might fail at runtime since those values might be undefined.
  let dict' = if isFunction val then dict else M.empty
  case e of
    -- Typed declarations
    (ident, (val', Just (ty, checkType))) -> do
      -- Kind check
      kind <- liftCheck $ kindOf moduleName ty
      guardWith (strMsg $ "Expected type of kind *, was " ++ prettyPrintKind kind) $ kind == Star
      -- Check the type with the new names in scope
      ty' <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty
      val'' <- bindNames dict' $ if checkType
                               then TypedValue true <$> check val' ty' <*> pure ty'
                               else return (TypedValue false val' ty')
      return (ident, (val'', ty'))
    -- Untyped declarations
    (ident, (val', Nothing)) -> do
      -- Infer the type with the new names in scope
      TypedValue _ val'' ty <- bindNames dict' $ infer val'
      ty =?= fromMaybe (error "name not found in dictionary") (lookup ident untypedDict)
      return (ident, (TypedValue true val'' ty, ty))

-- |
-- Check if a value introduces a function
--
isFunction :: Value -> Bool
isFunction (Abs _ _) = true
isFunction (TypedValue _ val _) = isFunction val
isFunction (PositionedValue _ val) = isFunction val
isFunction _ = false

-- |
-- Check if a value contains a type annotation
--
isTyped :: (Ident, Value) -> (Ident, (Value, Maybe (Type, Bool)))
isTyped (name, TypedValue checkType value ty) = (name, (value, Just (ty, checkType)))
isTyped (name, value) = (name, (value, Nothing))

-- |
-- Map a function over type annotations appearing inside a value
--
overTypes :: (Type -> Type) -> Value -> Value
overTypes f = let (_, f', _) = everywhereOnValues id g id in f'
  where
  g :: Value -> Value
  g (TypedValue checkTy val t) = TypedValue checkTy val (f t)
  g (TypeClassDictionary b (nm, tys) sco) = TypeClassDictionary b (nm, map f tys) sco
  g other = other

-- |
-- Replace type class dictionary placeholders with inferred type class dictionaries
--
replaceTypeClassDictionaries :: ModuleName -> Value -> Check Value
replaceTypeClassDictionaries mn =
  let (_, f, _) = everywhereOnValuesTopDownM return go return
  in f
  where
  go (TypeClassDictionary trySuperclasses constraint dicts) = do
    env <- getEnv
    entails env mn dicts constraint trySuperclasses
  go other = return other

-- |
-- A simplified representation of expressions which are used to represent type
-- class dictionaries at runtime, which can be compared for equality
--
data DictionaryValue
  -- |
  -- A dictionary which is brought into scope by a local constraint
  --
  = LocalDictionaryValue (Qualified Ident)
  -- |
  -- A dictionary which is brought into scope by an instance declaration
  --
  | GlobalDictionaryValue (Qualified Ident)
  -- |
  -- A dictionary which depends on other dictionaries
  --
  | DependentDictionaryValue (Qualified Ident) [DictionaryValue]
  -- |
  -- A subclass dictionary
  --
  | SubclassDictionaryValue DictionaryValue (Qualified ProperName) Integer
  deriving (Show, Ord, Eq)

-- |
-- Check that the current set of type class dictionaries entail the specified type class goal, and, if so,
-- return a type class dictionary reference.
--
entails :: Environment -> ModuleName -> [TypeClassDictionaryInScope] -> (Qualified ProperName, [Type]) -> Bool -> Check Value
entails env moduleName context = solve (sortedNubBy canonicalizeDictionary (filter filterModule context))
  where
    sortedNubBy :: (Ord k) => (v -> k) -> [v] -> [v]
    sortedNubBy f vs = M.elems (M.fromList (map (f &&& id) vs))

    -- Filter out type dictionaries which are in scope in the current module
    filterModule :: TypeClassDictionaryInScope -> Bool
    filterModule (TypeClassDictionaryInScope { tcdName = Qualified (Just mn) _ }) | mn == moduleName = true
    filterModule (TypeClassDictionaryInScope { tcdName = Qualified Nothing _ }) = true
    filterModule _ = false

    solve context' (className, tys) trySuperclasses =
      let
        dicts = go trySuperclasses className tys
      in case sortedNubBy dictTrace (chooseSimplestDictionaries dicts) of
           [] -> throwError <<< strMsg $ "No instance found for " ++ show className ++ " " ++ unwords (map prettyPrintTypeAtom tys)
           [_] -> return $ dictionaryValueToValue $ head dicts
           _ -> throwError <<< strMsg $ "Overlapping instances found for " ++ show className ++ " " ++ unwords (map prettyPrintTypeAtom tys)
      where
	  go trySuperclasses' className' tys' =
	    -- Look for regular type instances
	    [ mkDictionary (canonicalizeDictionary tcd) args
	    | tcd <- context'
	    -- Make sure the type class name matches the one we are trying to satisfy
	    , className' == tcdClassName tcd
	    -- Make sure the type unifies with the type in the type instance definition
	    , subst <- maybeToList <<< (>>= verifySubstitution) <<< fmap concat $ zipWithM (typeHeadsAreEqual moduleName env) tys' (tcdInstanceTypes tcd)
	    -- Solve any necessary subgoals
	    , args <- solveSubgoals subst (tcdDependencies tcd) ] ++

	    -- Look for implementations via superclasses
	    [ SubclassDictionaryValue suDict superclass index
	    | trySuperclasses'
	    , (subclassName, (args, _, implies)) <- M.toList (typeClasses env)
	    -- Try each superclass
	    , (index, (superclass, suTyArgs)) <- zip [0..] implies
	    -- Make sure the type class name matches the superclass name
	    , className' == superclass
	    -- Make sure the types unify with the types in the superclass implication
	    , subst <- maybeToList <<< (>>= verifySubstitution) <<< fmap concat $ zipWithM (typeHeadsAreEqual moduleName env) tys' suTyArgs
	    -- Finally, satisfy the subclass constraint
	    , args' <- maybeToList $ mapM (flip lookup subst) args
	    , suDict <- go true subclassName args' ]

	  -- Create dictionaries for subgoals which still need to be solved by calling go recursively
	  -- E.g. the goal (Show a, Show b) => Show (Either a b) can be satisfied if the current type
	  -- unifies with Either a b, and we can satisfy the subgoals Show a and Show b recursively.
	  solveSubgoals :: [(String, Type)] -> Maybe [(Qualified ProperName, [Type])] -> [Maybe [DictionaryValue]]
	  solveSubgoals _ Nothing = return Nothing
	  solveSubgoals subst (Just subgoals) = do
	    dict <- mapM (uncurry (go true) <<< second (map (replaceAllTypeVars subst))) subgoals
	    return $ Just dict
	  -- Make a dictionary from subgoal dictionaries by applying the correct function
	  mkDictionary :: Qualified Ident -> Maybe [DictionaryValue] -> DictionaryValue
	  mkDictionary fnName Nothing = LocalDictionaryValue fnName
	  mkDictionary fnName (Just []) = GlobalDictionaryValue fnName
	  mkDictionary fnName (Just dicts) = DependentDictionaryValue fnName dicts
	  -- Turn a DictionaryValue into a Value
	  dictionaryValueToValue :: DictionaryValue -> Value
	  dictionaryValueToValue (LocalDictionaryValue fnName) = Var fnName
	  dictionaryValueToValue (GlobalDictionaryValue fnName) = App (Var fnName) (ObjectLiteral [])
	  dictionaryValueToValue (DependentDictionaryValue fnName dicts) = foldl App (Var fnName) (map dictionaryValueToValue dicts)
	  dictionaryValueToValue (SubclassDictionaryValue dict superclassName index) =
	    App (Accessor (show superclassName ++ "_" ++ show index)
	                  (Accessor C.__superclasses (dictionaryValueToValue dict)))
	        (ObjectLiteral [])
	  -- Ensure that a substitution is valid
	  verifySubstitution :: [(String, Type)] -> Maybe [(String, Type)]
	  verifySubstitution subst = do
	    let grps = groupBy ((==) `on` fst) subst
	    guard (all (pairwise (unifiesWith env) <<< map snd) grps)
	    return $ map head grps
	  -- Choose the simplest DictionaryValues from a list of candidates
	  -- The reason for this function is as follows:
	  -- When considering overlapping instances, we don't want to consider the same dictionary
	  -- to be an overlap of itself when obtained as a superclass of another class.
	  -- Observing that we probably don't want to select a superclass instance when an instance
	  -- is available directly, and that there is no way for a superclass instance to actually
	  -- introduce an overlap that wouldn't have been there already, we simply remove dictionaries
	  -- obtained as superclass instances if there are simpler instances available.
	  chooseSimplestDictionaries :: [DictionaryValue] -> [DictionaryValue]
	  chooseSimplestDictionaries ds = case filter isSimpleDictionaryValue ds of
	                                    [] -> ds
	                                    simple -> simple
	  isSimpleDictionaryValue SubclassDictionaryValue{} = false
	  isSimpleDictionaryValue (DependentDictionaryValue _ ds) = all isSimpleDictionaryValue ds
	  isSimpleDictionaryValue _ = true
	  -- |
	  -- Get the "trace" of a DictionaryValue - that is, remove all SubclassDictionaryValue
	  -- data constructors
	  --
	  dictTrace :: DictionaryValue -> DictionaryValue
	  dictTrace (DependentDictionaryValue fnName dicts) = DependentDictionaryValue fnName $ map dictTrace dicts
	  dictTrace (SubclassDictionaryValue dict _ _) = dictTrace dict
	  dictTrace other = other

-- |
-- Check all values in a list pairwise match a predicate
--
pairwise :: (a -> a -> Bool) -> [a] -> Bool
pairwise _ [] = true
pairwise _ [_] = true
pairwise p (x : xs) = all (p x) xs && pairwise p xs

-- |
-- Check that two types unify
--
unifiesWith :: Environment -> Type -> Type -> Bool
unifiesWith _ (TUnknown u1) (TUnknown u2) | u1 == u2 = true
unifiesWith _ (Skolem _ s1 _) (Skolem _ s2 _) | s1 == s2 = true
unifiesWith _ (TypeVar v1) (TypeVar v2) | v1 == v2 = true
unifiesWith _ (TypeConstructor c1) (TypeConstructor c2) | c1 == c2 = true
unifiesWith e (TypeApp h1 t1) (TypeApp h2 t2) = unifiesWith e h1 h2 && unifiesWith e t1 t2
unifiesWith e (SaturatedTypeSynonym name args) t2 =
  case expandTypeSynonym' e name args of
    Left  _  -> false
    Right t1 -> unifiesWith e t1 t2
unifiesWith e t1 t2@(SaturatedTypeSynonym _ _) = unifiesWith e t2 t1
unifiesWith _ _ _ = false

-- |
-- Check whether the type heads of two types are equal (for the purposes of type class dictionary lookup),
-- and return a substitution from type variables to types which makes the type heads unify.
--
typeHeadsAreEqual :: ModuleName -> Environment -> Type -> Type -> Maybe [(String, Type)]
typeHeadsAreEqual _ _ (Skolem _ s1 _) (Skolem _ s2 _) | s1 == s2 = Just []
typeHeadsAreEqual _ _ t (TypeVar v) = Just [(v, t)]
typeHeadsAreEqual _ _ (TypeConstructor c1) (TypeConstructor c2) | c1 == c2 = Just []
typeHeadsAreEqual m e (TypeApp h1 t1) (TypeApp h2 t2) = (++) <$> typeHeadsAreEqual m e h1 h2 <*> typeHeadsAreEqual m e t1 t2
typeHeadsAreEqual m e (SaturatedTypeSynonym name args) t2 = case expandTypeSynonym' e name args of
  Left  _  -> Nothing
  Right t1 -> typeHeadsAreEqual m e t1 t2
typeHeadsAreEqual _ _ _ _ = Nothing

-- |
-- Ensure skolem variables do not escape their scope
--
skolemEscapeCheck :: Value -> Check {}
skolemEscapeCheck (TypedValue false _ _) = return {}
skolemEscapeCheck root@TypedValue{} =
  -- Every skolem variable is created when a ForAll type is skolemized.
  -- This determines the scope of that skolem variable, which is copied from the SkolemScope
  -- field of the ForAll constructor.
  -- We traverse the tree top-down, and collect any SkolemScopes introduced by ForAlls.
  -- If a Skolem is encountered whose SkolemScope is not in the current list, we have found
  -- an escaped skolem variable.
  let (_, f, _, _, _) = everythingWithContextOnValues [] [] (++) def go def def def
  in case f root of
       [] -> return {}
       ((binding, val) : _) -> throwError $ mkErrorStack ("Rigid/skolem type variable " ++ maybe "" (("bound by " ++) <<< prettyPrintValue) binding ++ " has escaped.") (Just (ValueError val))
  where
  def s _ = (s, [])

  go :: [(SkolemScope, Value)] -> Value -> ([(SkolemScope, Value)], [(Maybe Value, Value)])
  go scos val@(TypedValue _ _ (ForAll _ _ (Just sco))) = ((sco, val) : scos, [])
  go scos val@(TypedValue _ _ ty) = case collectSkolems ty \\ map fst scos of
                                      (sco : _) -> (scos, [(findBindingScope sco, val)])
                                      _ -> (scos, [])
    where
    collectSkolems :: Type -> [SkolemScope]
    collectSkolems = nub <<< everythingOnTypes (++) collect
      where
      collect (Skolem _ _ scope) = [scope]
      collect _ = []
  go scos _ = (scos, [])
  findBindingScope :: SkolemScope -> Maybe Value
  findBindingScope sco =
    let (_, f, _, _, _) = everythingOnValues mappend (const mempty) go' (const mempty) (const mempty) (const mempty)
    in getFirst $ f root
    where
    go' val@(TypedValue _ _ (ForAll _ _ (Just sco'))) | sco == sco' = First (Just val)
    go' _ = mempty
skolemEscapeCheck val = throwError $ mkErrorStack "Untyped value passed to skolemEscapeCheck" (Just (ValueError val))
-}
-- |
-- Ensure a row contains no duplicate labels
--
setify :: Type -> Type
setify = rowFromList <<< first (M.toList <<< M.fromList) <<< rowToList

-- |
-- \"Setify\" all rows occuring inside a value
--
setifyAll :: Type -> Type
setifyAll = everywhereOnTypes setify

-- |
-- Replace outermost unsolved unification variables with named type variables
--
varIfUnknown :: Type -> Type
varIfUnknown ty =
  let unks = nub $ unknowns ty
      toName x = "t" ++ show x
      ty' = everywhereOnTypes typeToVar ty
      typeToVar :: Type -> Type
      typeToVar (TUnknown u) = TypeVar (toName u)
      typeToVar t = t
  in mkForAll (sort <<< map toName $ unks) ty'

-- |
-- Remove any ForAlls and ConstrainedType constructors in a type by introducing new unknowns
-- or TypeClassDictionary values.
--
-- This is necessary during type checking to avoid unifying a polymorphic type with a
-- unification variable.
--
instantiatePolyTypeWithUnknowns :: Value -> Type -> UnifyT Type Check (Tuple Value Type)
instantiatePolyTypeWithUnknowns val (ForAll ident ty _) = do
  ty' <- replaceVarWithUnknown ident ty
  instantiatePolyTypeWithUnknowns val ty'
instantiatePolyTypeWithUnknowns val (ConstrainedType constraints ty) = do
   dicts <- getTypeClassDictionaries
   Tuple _ ty' <- instantiatePolyTypeWithUnknowns (error "Types under a constraint cannot themselves be constrained") ty
   return $ Tuple (foldl App val (map (flip (TypeClassDictionary true) dicts) constraints)) ty'
instantiatePolyTypeWithUnknowns val ty = return $ Tuple val ty

-- |
-- Replace a single type variable with a new unification variable
--
replaceVarWithUnknown :: String -> Type -> UnifyT Type Check Type
replaceVarWithUnknown ident ty = do
  tu <- fresh
  return $ replaceTypeVars ident tu ty

-- |
-- Replace fully applied type synonyms with the @SaturatedTypeSynonym@ data constructor, which helps generate
-- better error messages during unification.
--
replaceAllTypeSynonyms' :: Environment -> Type -> Either String Type
replaceAllTypeSynonyms' (Environment e) d =
  let syns = map (\(Tuple name (Tuple args _)) -> (Tuple name (length args))) $ M.toList e.typeSynonyms
  in saturateAllTypeSynonyms syns d

replaceAllTypeSynonyms :: forall e m. (Error e, Functor m, Monad m, MonadState CheckState m, MonadError e m) => WithErrorType e -> Type -> m Type
replaceAllTypeSynonyms errorType d = do
  env <- getEnv
  either (throwError <<< withErrorType errorType <<< strMsg) return $ replaceAllTypeSynonyms' env d

-- |
-- \"Desaturate\" @SaturatedTypeSynonym@s
--
desaturateAllTypeSynonyms :: Type -> Type
desaturateAllTypeSynonyms = everywhereOnTypes replaceSaturatedTypeSynonym
  where
  replaceSaturatedTypeSynonym (SaturatedTypeSynonym name args) = foldl TypeApp (TypeConstructor name) args
  replaceSaturatedTypeSynonym t = t

-- |
-- Replace a type synonym and its arguments with the aliased type
--
expandTypeSynonym' :: Environment -> Qualified ProperName -> [Type] -> Either String Type
expandTypeSynonym' env@(Environment e) name args =
  case M.lookup name e.typeSynonyms of
    Just (Tuple synArgs body) -> do
      let repl = replaceAllTypeVars (zip synArgs args) body
      replaceAllTypeSynonyms' env repl
    Nothing -> theImpossibleHappened "Type synonym was not defined"

expandTypeSynonym :: forall e m. (Error e, Monad m, MonadState CheckState m, MonadError e m) => WithErrorType e -> Qualified ProperName -> [Type] -> m Type
expandTypeSynonym errorType name args = do
  env <- getEnv
  either (\x -> throwError $ withErrorType errorType $ strMsg x) return $ expandTypeSynonym' env name args

expandAllTypeSynonyms :: forall e m. (Error e, Monad m, MonadState CheckState m, MonadError e m) => WithErrorType e -> Type -> m Type
expandAllTypeSynonyms errorType = everywhereOnTypesTopDownM go
  where
  go (SaturatedTypeSynonym name args) = expandTypeSynonym errorType name args
  go other = return other
{-
-- |
-- Ensure a set of property names and value does not contain duplicate labels
--
ensureNoDuplicateProperties :: (Error e, MonadError e m) => [(String, Value)] -> m {}
ensureNoDuplicateProperties ps = guardWith (strMsg "Duplicate property names") $ length (nub <<< map fst $ ps) == length ps

-- |
-- Infer a type for a value, rethrowing any error to provide a more useful error message
--
infer :: Value -> UnifyT Type Check Value
infer val = rethrow (mkErrorStack "Error inferring type of value" (Just (ValueError val)) <>) $ infer' val

-- |
-- Infer a type for a value
--
infer' :: Value -> UnifyT Type Check Value
infer' v@(NumericLiteral _) = return $ TypedValue true v tyNumber
infer' v@(StringLiteral _) = return $ TypedValue true v tyString
infer' v@(BooleanLiteral _) = return $ TypedValue true v tyBoolean
infer' (ArrayLiteral vals) = do
  ts <- mapM infer vals
  els <- fresh
  forM_ ts $ \(TypedValue _ _ t) -> els =?= TypeApp tyArray t
  return $ TypedValue true (ArrayLiteral ts) els
infer' (ObjectLiteral ps) = do
  ensureNoDuplicateProperties ps
  ts <- mapM (infer <<< snd) ps
  let fields = zipWith (\name (TypedValue _ _ t) -> (name, t)) (map fst ps) ts
      ty = TypeApp tyObject $ rowFromList (fields, REmpty)
  return $ TypedValue true (ObjectLiteral (zip (map fst ps) ts)) ty
infer' (ObjectUpdate o ps) = do
  ensureNoDuplicateProperties ps
  row <- fresh
  newVals <- zipWith (\(name, _) t -> (name, t)) ps <$> mapM (infer <<< snd) ps
  let newTys = map (\(name, TypedValue _ _ ty) -> (name, ty)) newVals
  oldTys <- zip (map fst ps) <$> replicateM (length ps) fresh
  let oldTy = TypeApp tyObject $ rowFromList (oldTys, row)
  o' <- TypedValue true <$> check o oldTy <*> pure oldTy
  return $ TypedValue true (ObjectUpdate o' newVals) $ TypeApp tyObject $ rowFromList (newTys, row)
infer' (Accessor prop val) = do
  typed@(TypedValue _ _ objTy) <- infer val
  propTy <- inferProperty objTy prop
  case propTy of
    Nothing -> do
      field <- fresh
      rest <- fresh
      _ <- subsumes Nothing objTy (TypeApp tyObject (RCons prop field rest))
      return $ TypedValue true (Accessor prop typed) field
    Just ty -> return $ TypedValue true (Accessor prop typed) ty
infer' (Abs (Left arg) ret) = do
  ty <- fresh
  Just moduleName <- checkCurrentModule <$> get
  bindLocalVariables moduleName [(arg, ty)] $ do
    body@(TypedValue _ _ bodyTy) <- infer' ret
    return $ TypedValue true (Abs (Left arg) body) $ function ty bodyTy
infer' (Abs (Right _) _) = error "Binder was not desugared"
infer' (App f arg) = do
  f'@(TypedValue _ _ ft) <- infer f
  (ret, app) <- checkFunctionApplication f' ft arg Nothing
  return $ TypedValue true app ret
infer' (Var var) = do
  Just moduleName <- checkCurrentModule <$> get
  ty <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< lookupVariable moduleName $ var
  case ty of
    ConstrainedType constraints ty' -> do
      dicts <- getTypeClassDictionaries
      return $ TypedValue true (foldl App (Var var) (map (flip (TypeClassDictionary true) dicts) constraints)) ty'
    _ -> return $ TypedValue true (Var var) ty
infer' v@(Constructor c) = do
  env <- getEnv
  case M.lookup c (dataConstructors env) of
    Nothing -> throwError <<< strMsg $ "Constructor " ++ show c ++ " is undefined"
    Just (_, ty) -> do ty' <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty
                       return $ TypedValue true v ty'
infer' (Case vals binders) = do
  ts <- mapM infer vals
  ret <- fresh
  binders' <- checkBinders (map (\(TypedValue _ _ t) -> t) ts) ret binders
  return $ TypedValue true (Case ts binders') ret
infer' (IfThenElse cond th el) = do
  cond' <- check cond tyBoolean
  v2@(TypedValue _ _ t2) <- infer th
  v3@(TypedValue _ _ t3) <- infer el
  t2 =?= t3
  return $ TypedValue true (IfThenElse cond' v2 v3) t2
infer' (Let ds val) = do
  (ds', val'@(TypedValue _ _ valTy)) <- inferLetBinding [] ds val infer
  return $ TypedValue true (Let ds' val') valTy
infer' (SuperClassDictionary className tys) = do
  dicts <- getTypeClassDictionaries
  return $ TypeClassDictionary false (className, tys) dicts
infer' (TypedValue checkType val ty) = do
  Just moduleName <- checkCurrentModule <$> get
  kind <- liftCheck $ kindOf moduleName ty
  guardWith (strMsg $ "Expected type of kind *, was " ++ prettyPrintKind kind) $ kind == Star
  ty' <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty
  val' <- if checkType then check val ty' else return val
  return $ TypedValue true val' ty'
infer' (PositionedValue pos val) = rethrowWithPosition pos $ infer' val
infer' _ = error "Invalid argument to infer"

inferLetBinding :: [Declaration] -> [Declaration] -> Value -> (Value -> UnifyT Type Check Value) -> UnifyT Type Check ([Declaration], Value)
inferLetBinding seen [] ret j = (,) seen <$> j ret
inferLetBinding seen (ValueDeclaration ident nameKind [] Nothing tv@(TypedValue checkType val ty) : rest) ret j = do
  Just moduleName <- checkCurrentModule <$> get
  kind <- liftCheck $ kindOf moduleName ty
  guardWith (strMsg $ "Expected type of kind *, was " ++ prettyPrintKind kind) $ kind == Star
  let dict = if isFunction val then M.singleton (moduleName, ident) (ty, nameKind) else M.empty
  ty' <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty
  TypedValue _ val' ty'' <- if checkType then bindNames dict (check val ty') else return tv
  bindNames (M.singleton (moduleName, ident) (ty'', nameKind)) $ inferLetBinding (seen ++ [ValueDeclaration ident nameKind [] Nothing (TypedValue checkType val' ty'')]) rest ret j
inferLetBinding seen (ValueDeclaration ident nameKind [] Nothing val : rest) ret j = do
  valTy <- fresh
  Just moduleName <- checkCurrentModule <$> get
  let dict = if isFunction val then M.singleton (moduleName, ident) (valTy, nameKind) else M.empty
  TypedValue _ val' valTy' <- bindNames dict $ infer val
  valTy =?= valTy'
  bindNames (M.singleton (moduleName, ident) (valTy', nameKind)) $ inferLetBinding (seen ++ [ValueDeclaration ident nameKind [] Nothing val']) rest ret j
inferLetBinding seen (BindingGroupDeclaration ds : rest) ret j = do
  Just moduleName <- checkCurrentModule <$> get
  (es, dict, untypedDict) <- typeDictionaryForBindingGroup moduleName (map (\(i, _, v) -> (i, v)) ds)
  ds' <- forM es $ \e -> do
    (ident, (val', _)) <- typeForBindingGroupElement moduleName e dict untypedDict
    return $ (ident, LocalVariable, val')
  bindNames dict $ inferLetBinding (seen ++ [BindingGroupDeclaration ds']) rest ret j
inferLetBinding seen (PositionedDeclaration pos d : ds) ret j = rethrowWithPosition pos $ do
  ((d' : ds'), val') <- inferLetBinding seen (d : ds) ret j
  return (PositionedDeclaration pos d' : ds', val')
inferLetBinding _ _ _ _ = error "Invalid argument to inferLetBinding"

-- |
-- Infer the type of a property inside a record with a given type
--
inferProperty :: Type -> String -> UnifyT Type Check (Maybe Type)
inferProperty (TypeApp obj row) prop | obj == tyObject = do
  let (props, _) = rowToList row
  return $ lookup prop props
inferProperty (SaturatedTypeSynonym name args) prop = do
  replaced <- introduceSkolemScope <=< expandTypeSynonym name $ args
  inferProperty replaced prop
inferProperty (ForAll ident ty _) prop = do
  replaced <- replaceVarWithUnknown ident ty
  inferProperty replaced prop
inferProperty _ _ = return Nothing

-- |
-- Infer the types of variables brought into scope by a binder
--
inferBinder :: Type -> Binder -> UnifyT Type Check (M.Map Ident Type)
inferBinder _ NullBinder = return M.empty
inferBinder val (StringBinder _) = val =?= tyString >> return M.empty
inferBinder val (NumberBinder _) = val =?= tyNumber >> return M.empty
inferBinder val (BooleanBinder _) = val =?= tyBoolean >> return M.empty
inferBinder val (VarBinder name) = return $ M.singleton name val
inferBinder val (ConstructorBinder ctor binders) = do
  env <- getEnv
  case M.lookup ctor (dataConstructors env) of
    Just (_, ty) -> do
      (_, fn) <- instantiatePolyTypeWithUnknowns (error "Data constructor types cannot contain constraints") ty
      fn' <- replaceAllTypeSynonyms fn
      go binders fn'
        where
        go [] ty' = do
          _ <- subsumes Nothing val ty'
          return M.empty
        go (binder : binders') (TypeApp (TypeApp t obj) ret) | t == tyFunction =
          M.union <$> inferBinder obj binder <*> go binders' ret
        go _ _ = throwError <<< strMsg $ "Wrong number of arguments to constructor " ++ show ctor
    _ -> throwError <<< strMsg $ "Constructor " ++ show ctor ++ " is not defined"
inferBinder val (ObjectBinder props) = do
  row <- fresh
  rest <- fresh
  m1 <- inferRowProperties row rest props
  val =?= TypeApp tyObject row
  return m1
  where
  inferRowProperties :: Type -> Type -> [(String, Binder)] -> UnifyT Type Check (M.Map Ident Type)
  inferRowProperties nrow row [] = nrow =?= row >> return M.empty
  inferRowProperties nrow row ((name, binder):binders) = do
    propTy <- fresh
    m1 <- inferBinder propTy binder
    m2 <- inferRowProperties nrow (RCons name propTy row) binders
    return $ m1 `M.union` m2
inferBinder val (ArrayBinder binders) = do
  el <- fresh
  m1 <- M.unions <$> mapM (inferBinder el) binders
  val =?= TypeApp tyArray el
  return m1
inferBinder val (ConsBinder headBinder tailBinder) = do
  el <- fresh
  m1 <- inferBinder el headBinder
  m2 <- inferBinder val tailBinder
  val =?= TypeApp tyArray el
  return $ m1 `M.union` m2
inferBinder val (NamedBinder name binder) = do
  m <- inferBinder val binder
  return $ M.insert name val m
inferBinder val (PositionedBinder pos binder) =
  rethrowWithPosition pos $ inferBinder val binder

-- |
-- Check the types of the return values in a set of binders in a case statement
--
checkBinders :: [Type] -> Type -> [CaseAlternative] -> UnifyT Type Check [CaseAlternative]
checkBinders _ _ [] = return []
checkBinders nvals ret (CaseAlternative binders grd val : bs) = do
  Just moduleName <- checkCurrentModule <$> get
  m1 <- M.unions <$> zipWithM inferBinder nvals binders
  r <- bindLocalVariables moduleName (M.toList m1) $ do
    val' <- TypedValue true <$> check val ret <*> pure ret
    case grd of
      Nothing -> return $ CaseAlternative binders Nothing val'
      Just g -> do
        g' <- check g tyBoolean
        return $ CaseAlternative binders (Just g') val'
  rs <- checkBinders nvals ret bs
  return $ r : rs
-}

-- |
-- Generate a new skolem constant
--
newSkolemConstant :: UnifyT Type Check Number
newSkolemConstant = fresh'

-- |
-- Generate a new skolem scope
--
newSkolemScope :: UnifyT Type Check SkolemScope
newSkolemScope = SkolemScope <$> fresh'

-- |
-- Skolemize a type variable by replacing its instances with fresh skolem constants
--
skolemize :: String -> Number -> SkolemScope -> Type -> Type
skolemize ident sko scope = replaceTypeVars ident (Skolem ident sko scope)

-- |
-- This function has one purpose - to skolemize type variables appearing in a
-- SuperClassDictionary placeholder. These type variables are somewhat unique since they are the
-- only example of scoped type variables.
--
skolemizeTypesInValue :: String -> Number -> SkolemScope -> Value -> Value
skolemizeTypesInValue ident sko scope = case everywhereOnValues id go id of
    Tuple3 _ f _ -> f
  where
  go (SuperClassDictionary c ts) = SuperClassDictionary c (map (skolemize ident sko scope) ts)
  go other = other

-- |
-- Introduce skolem scope at every occurence of a ForAll
--
introduceSkolemScope :: Type -> UnifyT Type Check Type
introduceSkolemScope = everywhereOnTypesM go
  where
  go (ForAll ident ty Nothing) = ForAll ident ty <$> (Just <$> newSkolemScope)
  go other = return other
{-
-- |
-- Check the type of a value, rethrowing errors to provide a better error message
--
check :: Value -> Type -> UnifyT Type Check Value
check val ty = rethrow (\x -> mkErrorStack errorMessage (Just (ValueError val)) <> x) $ check' val ty
  where
  errorMessage =
    "Error checking type of term " ++
    prettyPrintValue val ++
    " against type " ++
    prettyPrintType ty

-- |
-- Check the type of a value
--
check' :: Value -> Type -> UnifyT Type Check Value
check' val (ForAll ident ty _) = do
  scope <- newSkolemScope
  sko <- newSkolemConstant
  let sk = skolemize ident sko scope ty
  let skVal = skolemizeTypesInValue ident sko scope val
  val' <- check skVal sk
  return $ TypedValue true val' (ForAll ident ty (Just scope))
check' val t@(ConstrainedType constraints ty) = do
  dictNames <- forM constraints $ \(Qualified _ (ProperName className), _) -> do
    n <- liftCheck freshDictionaryName
    return $ Ident $ "__dict_" ++ className ++ "_" ++ show n
  val' <- withTypeClassDictionaries (zipWith (\name (className, instanceTy) ->
    TypeClassDictionaryInScope name className instanceTy Nothing TCDRegular) (map (Qualified Nothing) dictNames)
      constraints) $ check val ty
  return $ TypedValue true (foldr (Abs <<< Left) val' dictNames) t
check' val (SaturatedTypeSynonym name args) = do
  ty <- introduceSkolemScope <=< expandTypeSynonym name $ args
  check val ty
check' val u@(TUnknown _) = do
  val'@(TypedValue _ _ ty) <- infer val
  -- Don't unify an unknown with an inferred polytype
  (val'', ty') <- instantiatePolyTypeWithUnknowns val' ty
  ty' =?= u
  return $ TypedValue true val'' ty'
check' v@(NumericLiteral _) t | t == tyNumber =
  return $ TypedValue true v t
check' v@(StringLiteral _) t | t == tyString =
  return $ TypedValue true v t
check' v@(BooleanLiteral _) t | t == tyBoolean =
  return $ TypedValue true v t
check' (ArrayLiteral vals) t@(TypeApp a ty) = do
  a =?= tyArray
  array <- ArrayLiteral <$> forM vals (`check` ty)
  return $ TypedValue true array t
check' (Abs (Left arg) ret) ty@(TypeApp (TypeApp t argTy) retTy) | t == tyFunction = do
  Just moduleName <- checkCurrentModule <$> get
  ret' <- bindLocalVariables moduleName [(arg, argTy)] $ check ret retTy
  return $ TypedValue true (Abs (Left arg) ret') ty
check' (Abs (Right _) _) _ = error "Binder was not desugared"
check' (App f arg) ret = do
  f'@(TypedValue _ _ ft) <- infer f
  (_, app) <- checkFunctionApplication f' ft arg (Just ret)
  return $ TypedValue true app ret
check' v@(Var var) ty = do
  Just moduleName <- checkCurrentModule <$> get
  repl <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< lookupVariable moduleName $ var
  ty' <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty
  v' <- subsumes (Just v) repl ty'
  case v' of
    Nothing -> throwError <<< strMsg $ "Unable to check type subsumption"
    Just v'' -> return $ TypedValue true v'' ty'
check' (SuperClassDictionary className tys) _ = do
  {-
  -- Here, we replace a placeholder for a superclass dictionary with a regular
  -- TypeClassDictionary placeholder. The reason we do this is that it is necessary to have the
  -- correct super instance dictionaries in scope, and these are not available when the type class
  -- declaration gets desugared.
  --
  -- Note also that the first argument to TypeClassDictionary is false, meaning we _do not_ want
  -- to consider superclass instances when searching for this dictionary - doing so might lead
  -- to traversing a cycle in the instance graph.
  -}
  dicts <- getTypeClassDictionaries
  return $ TypeClassDictionary false (className, tys) dicts
check' (TypedValue checkType val ty1) ty2 = do
  Just moduleName <- checkCurrentModule <$> get
  kind <- liftCheck $ kindOf moduleName ty1
  guardWith (strMsg $ "Expected type of kind *, was " ++ prettyPrintKind kind) $ kind == Star
  ty1' <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty1
  val' <- subsumes (Just val) ty1' ty2
  case val' of
    Nothing -> throwError <<< strMsg $ "Unable to check type subsumption"
    Just val'' -> do
      val''' <- if checkType then check val'' ty1' else return val''
      return $ TypedValue checkType (TypedValue true val''' ty1) ty2
check' (Case vals binders) ret = do
  vals' <- mapM infer vals
  let ts = map (\(TypedValue _ _ t) -> t) vals'
  binders' <- checkBinders ts ret binders
  return $ TypedValue true (Case vals' binders') ret
check' (IfThenElse cond th el) ty = do
  cond' <- check cond tyBoolean
  th' <- check th ty
  el' <- check el ty
  return $ TypedValue true (IfThenElse cond' th' el') ty
check' (ObjectLiteral ps) t@(TypeApp obj row) | obj == tyObject = do
  ensureNoDuplicateProperties ps
  ps' <- checkProperties ps row false
  return $ TypedValue true (ObjectLiteral ps') t
check' (ObjectUpdate obj ps) t@(TypeApp o row) | o == tyObject = do
  ensureNoDuplicateProperties ps
  us <- zip (map fst ps) <$> replicateM (length ps) fresh
  let (propsToCheck, rest) = rowToList row
      propsToRemove = map fst ps
      remainingProps = filter (\(p, _) -> p `notElem` propsToRemove) propsToCheck
  obj' <- check obj (TypeApp tyObject (rowFromList (us ++ remainingProps, rest)))
  ps' <- checkProperties ps row true
  return $ TypedValue true (ObjectUpdate obj' ps') t
check' (Accessor prop val) ty = do
  rest <- fresh
  val' <- check val (TypeApp tyObject (RCons prop ty rest))
  return $ TypedValue true (Accessor prop val') ty
check' (Constructor c) ty = do
  env <- getEnv
  case M.lookup c (dataConstructors env) of
    Nothing -> throwError <<< strMsg $ "Constructor " ++ show c ++ " is undefined"
    Just (_, ty1) -> do
      repl <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty1
      _ <- subsumes Nothing repl ty
      return $ TypedValue true (Constructor c) ty
check' (Let ds val) ty = do
  (ds', val') <- inferLetBinding [] ds val (flip check ty)
  return $ TypedValue true (Let ds' val') ty
check' val ty | containsTypeSynonyms ty = do
  ty' <- introduceSkolemScope <=< expandAllTypeSynonyms $ ty
  check val ty'
check' (PositionedValue pos val) ty =
  rethrowWithPosition pos $ check val ty
check' val ty = throwError $ mkErrorStack ("Value does not have type " ++ prettyPrintType ty) (Just (ValueError val))
-}
containsTypeSynonyms :: Type -> Boolean
containsTypeSynonyms = everythingOnTypes (||) go where
  go (SaturatedTypeSynonym _ _) = true
  go _ = false
{-
-- |
-- Check the type of a collection of named record fields
--
-- The @lax@ parameter controls whether or not every record member has to be provided. For object updates, this is not the case.
--
checkProperties :: [(String, Value)] -> Type -> Bool -> UnifyT Type Check [(String, Value)]
checkProperties ps row lax = let (ts, r') = rowToList row in go ps ts r' where
  go [] [] REmpty = return []
  go [] [] u@(TUnknown _) = do u =?= REmpty
                               return []
  go [] [] (Skolem _ _ _) | lax = return []
  go [] ((p, _): _) _ | lax = return []
                      | otherwise = throwError $ mkErrorStack ("Object does not have property " ++ p) (Just (ValueError (ObjectLiteral ps)))
  go ((p,_):_) [] REmpty = throwError $ mkErrorStack ("Property " ++ p ++ " is not present in closed object type " ++ prettyPrintRow row) (Just (ValueError (ObjectLiteral ps)))
  go ((p,v):ps') [] u@(TUnknown _) = do
    v'@(TypedValue _ _ ty) <- infer v
    rest <- fresh
    u =?= RCons p ty rest
    ps'' <- go ps' [] rest
    return $ (p, v') : ps''
  go ((p,v):ps') ts r =
    case lookup p ts of
      Nothing -> do
        v'@(TypedValue _ _ ty) <- infer v
        rest <- fresh
        r =?= RCons p ty rest
        ps'' <- go ps' ts rest
        return $ (p, v') : ps''
      Just ty -> do
        v' <- check v ty
        ps'' <- go ps' (delete (p, ty) ts) r
        return $ (p, v') : ps''
  go _ _ _ = throwError $ mkErrorStack ("Object does not have type " ++ prettyPrintType (TypeApp tyObject row)) (Just (ValueError (ObjectLiteral ps)))

-- |
-- Check the type of a function application, rethrowing errors to provide a better error message
--
checkFunctionApplication :: Value -> Type -> Value -> Maybe Type -> UnifyT Type Check (Type, Value)
checkFunctionApplication fn fnTy arg ret = rethrow (mkErrorStack errorMessage (Just (ValueError fn)) <>) $ checkFunctionApplication' fn fnTy arg ret
  where
  errorMessage = "Error applying function of type "
    ++ prettyPrintType fnTy
    ++ " to argument " ++ prettyPrintValue arg

-- |
-- Check the type of a function application
--
checkFunctionApplication' :: Value -> Type -> Value -> Maybe Type -> UnifyT Type Check (Type, Value)
checkFunctionApplication' fn (TypeApp (TypeApp tyFunction' argTy) retTy) arg ret = do
  tyFunction' =?= tyFunction
  _ <- maybe (return Nothing) (subsumes Nothing retTy) ret
  subst <- unifyCurrentSubstitution <$> UnifyT get
  arg' <- check arg (subst $? argTy)
  return (retTy, App fn arg')
checkFunctionApplication' fn (ForAll ident ty _) arg ret = do
  replaced <- replaceVarWithUnknown ident ty
  checkFunctionApplication fn replaced arg ret
checkFunctionApplication' fn u@(TUnknown _) arg ret = do
  arg' <- do
    TypedValue _ arg' t <- infer arg
    (arg'', t') <- instantiatePolyTypeWithUnknowns arg' t
    return $ TypedValue true arg'' t'
  let ty = (\(TypedValue _ _ t) -> t) arg'
  ret' <- maybe fresh return ret
  u =?= function ty ret'
  return (ret', App fn arg')
checkFunctionApplication' fn (SaturatedTypeSynonym name tyArgs) arg ret = do
  ty <- introduceSkolemScope <=< expandTypeSynonym name $ tyArgs
  checkFunctionApplication fn ty arg ret
checkFunctionApplication' fn (ConstrainedType constraints fnTy) arg ret = do
  dicts <- getTypeClassDictionaries
  checkFunctionApplication' (foldl App fn (map (flip (TypeClassDictionary true) dicts) constraints)) fnTy arg ret
checkFunctionApplication' _ fnTy arg _ = throwError <<< strMsg $ "Cannot apply a function of type "
  ++ prettyPrintType fnTy
  ++ " to argument " ++ prettyPrintValue arg
-}

{-
-- |
-- Check whether one type subsumes another, rethrowing errors to provide a better error message
--
subsumes :: Maybe Value -> Type -> Type -> UnifyT Type Check (Maybe Value)
subsumes val ty1 ty2 = rethrow (\x -> mkErrorStack errorMessage (ValueError <$> val) <> x) $ subsumes' val ty1 ty2
  where
  errorMessage = "Error checking that type "
    ++ prettyPrintType ty1
    ++ " subsumes type "
    ++ prettyPrintType ty2

-- |
-- Check whether one type subsumes another
--
subsumes' :: Maybe Value -> Type -> Type -> UnifyT Type Check (Maybe Value)
subsumes' val (ForAll ident ty1 _) ty2 = do
  replaced <- replaceVarWithUnknown ident ty1
  subsumes val replaced ty2
subsumes' val ty1 (ForAll ident ty2 sco) =
  case sco of
    Just sco' -> do
      sko <- newSkolemConstant
      let sk = skolemize ident sko sco' ty2
      subsumes val ty1 sk
    Nothing -> throwError <<< strMsg $ "Skolem variable scope is unspecified"
subsumes' val (TypeApp (TypeApp f1 arg1) ret1) (TypeApp (TypeApp f2 arg2) ret2) | f1 == tyFunction && f2 == tyFunction = do
  _ <- subsumes Nothing arg2 arg1
  _ <- subsumes Nothing ret1 ret2
  return val
subsumes' val (SaturatedTypeSynonym name tyArgs) ty2 = do
  ty1 <- introduceSkolemScope <=< expandTypeSynonym unifyError name $ tyArgs
  subsumes val ty1 ty2
subsumes' val ty1 (SaturatedTypeSynonym name tyArgs) = do
  ty2 <- introduceSkolemScope <=< expandTypeSynonym unifyError name $ tyArgs
  subsumes val ty1 ty2
subsumes' (Just val) (ConstrainedType constraints ty1) ty2 = do
  dicts <- getTypeClassDictionaries
  _ <- subsumes' Nothing ty1 ty2
  return <<< Just $ foldl App val (map (flip (TypeClassDictionary true) dicts) constraints)
subsumes' val (TypeApp f1 r1) (TypeApp f2 r2) | f1 == tyObject && f2 == tyObject =
  case Tuple (rowToList r1) (rowToList r2) of
    Tuple (Tuple ts1 r1') (Tuple ts2 r2') ->
      let ts1' = sortBy (compare `on` fst) ts1
          ts2' = sortBy (compare `on` fst) ts2
      in do
        go ts1' ts2' r1' r2'
        return val
  where
  go [] ts2 r1' r2' = r1' =?= rowFromList (Tuple ts2 r2')
  go ts1 [] r1' r2' = r2' =?= rowFromList (Tuple ts1 r1')
  go ((Tuple p1 ty1) : ts1) ((Tuple p2 ty2) : ts2) r1' r2' = case unit of
      _ | p1 == p2 -> do
        _ <- subsumes Nothing ty1 ty2
        go ts1 ts2 r1' r2'
      _ | p1 < p2 -> do
        rest <- fresh
        r2' =?= RCons p1 ty1 rest
        go ts1 ((Tuple p2 ty2) : ts2) r1' rest
      _ -> do
        rest <- fresh
        r1' =?= RCons p2 ty2 rest
        go ((Tuple p1 ty1) : ts1) ts2 rest r2'
subsumes' val ty1 ty2@(TypeApp obj _) | obj == tyObject = subsumes val ty2 ty1
subsumes' val ty1 ty2 = do
  ty1 =?= ty2
  return val
-}

error: pathspec 'src/Language/PureScript/TypeChecker/Types.purs' did not match any file(s) known to git


Add test file

Add test file

module Data where

data A = A

func :: A -> A
func a = a

error: pathspec 'src/test/testData/Data.purs' did not match any file(s) known to git


Added initial incomplete MouseEvent module.

module DOM.Event.MouseEvent where

import Prelude
import DOM.Event.Types
import Unsafe.Coerce

foreign import data MouseEvent :: *

data MouseEventType = MouseEventType EventType
click

toEvent :: MouseEvent -> Event
toEvent = unsafeCoerce

foreign import movementX :: MouseEvent -> Number
foreign import movementY :: MouseEvent -> Number

foreign import mouseEventListener :: forall eff a. (MouseEvent -> Eff (eff) a) -> EventListener eff

addMouseEventListener :: MouseEventType

error: pathspec 'src/DOM/Event/MouseEvent.purs' did not match any file(s) known to git


Adding $cacheFactory and Cache

Adding $cacheFactory and Cache

module Angular.Cache
 ( Cache(..)
 , CacheFactory(..)
 , CACHE(..)
 , Key(..)
 , Name(..)
 , Options(..)
 , injCacheFactory
 , cache
 , put
 , get
 , remove
 , removeAll
 , destroy
 , info
 ) where

import Control.Monad.Eff
import Data.Maybe

import Angular.Injector (InjectDependency(..))

foreign import data CACHE :: !

foreign import data Cache :: *

foreign import data CacheFactory :: *

type Key = String

type Name = String

type Options a = { capacity :: Number | a }

foreign import injCacheFactory
  " function injCacheFactory(){ \
  \   var $injector = angular.element(document).injector(); \
  \   return $injector.get('$cacheFactory'); \
  \ } "
  :: forall e. Eff (nginj :: InjectDependency | e) CacheFactory

foreign import cache
  " function cache(name){ \
  \   return function(opts){ \
  \     return function($cacheFactory){ \
  \       return function(){ \
  \         return $cacheFactory(name, opts.values[0]); \
  \       }; \
  \     }; \
  \   }; \
  \ } "
  :: forall e a. Name -> Maybe (Options a) -> CacheFactory -> Eff (ngcache :: CACHE | e) Cache

foreign import put
  "  function put(key){ \
  \    return function(value){ \
  \      return function(cache){ \
  \        return function(){ \
  \          return cache.put(key, value); \
  \        }; \
  \      }; \
  \    }; \
  \  } "
  :: forall e a. Key -> a -> Cache -> Eff (ngcache :: CACHE | e) a

foreign import get
  "  function get(key){ \
  \    return function(cache){ \
  \      return function(){ \
  \        return cache.get(key); \
  \      }; \
  \    }; \
  \  } "
  :: forall e a. Key -> Cache -> Eff (ngcache :: CACHE | e) a

foreign import remove
  "  function remove(key){ \
  \    return function(cache){ \
  \      return function(){ \
  \        return cache.remove(key); \
  \      }; \
  \    }; \
  \  } "
  :: forall e a. Key -> Cache -> Eff (ngcache :: CACHE | e) Unit

foreign import removeAll
  "  function removeAll(cache){ \
  \    return function(){ \
  \      return cache.removeAll(); \
  \    }; \
  \  } "
  :: forall e. Cache -> Eff (ngcache :: CACHE | e) Unit

foreign import destroy
  "  function destroy(cache){ \
  \    return function(){ \
  \      return cache.destroy(); \
  \    }; \
  \  } "
  :: forall e. Cache -> Eff (ngcache :: CACHE | e) Unit

foreign import info
  "  function info(cache){ \
  \    return function(){ \
  \      return cache.info(); \
  \    }; \
  \  } "
  :: forall e a. Cache -> Eff (ngcache :: CACHE | e) { id :: String, size :: Number | a }

error: pathspec 'src/Angular/Cache.purs' did not match any file(s) known to git


remove testonly websocket

remove testonly websocket

module Openspace.Ui.Stream where

import Control.Monad.Eff
import Control.Monad.Eff.Console
import Control.Monad.ST
import DOM
import Data.Either
import Data.Foreign
import Openspace.Engine
import Openspace.Network.Socket
import Openspace.Types
import Openspace.Ui.Emitter
import Openspace.Ui.Parser
import Openspace.Ui.Render
import Prelude
import Rx.Observable

foreign import setDrag :: forall eff. Eff eff Unit
foreign import unsetDrag :: forall eff. Eff eff Unit

netStream :: forall eff. Socket -> Eff( net :: Net | eff ) (Observable Action)
netStream socket = do
  onReceive <- socketObserver socket
  return $ actionFromForeign parseAction id <$> (parseMessage <$> onReceive)

dragStream :: forall eff. Eff( dom :: DOM| eff) (Observable Action)
dragStream = do
  lookup <- emitterLookup <$> getEmitters
  let dragOverSlot = (\e -> case parseSlot (getDetail e) of
                         Right s -> AssignTopic s
                         Left err -> UnassignTopic
                     ) <$> lookup "dragOverSlot"

      dragOverTrash = const DeleteTopic <$> lookup "dragOverTrash"

      -- HTML 5 fires dragLeave before dragEnd occurs
      -- TODO: Find a cleaner solution
      dragLeave = delay 30 $ const UnassignTopic
                  <$> lookup "dragLeaveSlot" `merge` lookup "dragLeaveTrash"

      dragOver = dragLeave `merge` dragOverSlot `merge` dragOverTrash

      dragStart = parseTopic <<< getDetail
                  <$> lookup "dragStartTopic" `merge` lookup "dragStartGridTopic"
      dragEnd = lookup "dragEndTopic" `merge` lookup "dragEndGridTopic"

      {- rebindable "bind" does make this a lot easier -}
      dragTopic :: Observable Action
      dragTopic = do
        let bind = flatMapLatest
        t' <- dragStart
        case t' of
          Right t -> do
            action <- dragOver
            dragEnd
            return (action t)
          Left e -> return $ ShowError (show e)

  runObservable $ const setDrag <$> dragStart
  runObservable $ const unsetDrag <$> dragEnd

  return dragTopic

uiStream :: forall eff. Eff( dom :: DOM | eff ) (Observable Action)
uiStream = do
  lookup <- emitterLookup <$> getEmitters
  let addTopic    = (actionFromForeign parseTopic AddTopic) <<< getDetail
                    <$> lookup "addTopic"
      addRoom     = (actionFromForeign parseRoom AddRoom) <<< getDetail
                    <$> lookup "addRoom"
      deleteRoom  = (actionFromForeign parseRoom DeleteRoom) <<< getDetail
                    <$> lookup "deleteRoom"
      addBlock    = (actionFromForeign parseBlock AddBlock) <<< getDetail
                    <$> lookup "addBlock"
      deleteBlock = (actionFromForeign parseBlock DeleteBlock) <<< getDetail
                    <$> lookup "deleteBlock"
      changeGrid = addRoom `merge` deleteRoom `merge` addBlock `merge` deleteBlock
  dragTopic <- dragStream
  return $ addTopic `merge` dragTopic `merge` changeGrid

main = do
  socketUrl <- getSocketUrl
  -- Uncomment for testing:
  -- let sockEmitter = getSocket ("ws://frost.kritzcreek.me/socket/0")
  -- Comment for testing:
  let sockEmitter = getSocket ("ws://" ++ socketUrl)

  -- Initial State
  appSt <- newSTRef emptyState
  -- Initial Render
  renderMenu (show <$> topicTypes)
  readSTRef appSt >>= renderApp

  --Request Initial State
  emitRefresh sockEmitter

  -- Observable Action
  ui  <- uiStream
  net <- netStream sockEmitter
  -- Broadcast the UI Observable
  subscribe ui (\a -> case a of
                   ShowError e -> log e
                   _ -> emitAction sockEmitter (serialize a))
  -- Evaluate Actions from the Server
  subscribe net (\a -> modifySTRef appSt (evalAction a) >>= renderApp)

module Openspace.Ui.Stream where

import Control.Monad.Eff
import Control.Monad.Eff.Console
import Control.Monad.ST
import DOM
import Data.Either
import Data.Foreign
import Openspace.Engine
import Openspace.Network.Socket
import Openspace.Types
import Openspace.Ui.Emitter
import Openspace.Ui.Parser
import Openspace.Ui.Render
import Prelude
import Rx.Observable

foreign import setDrag :: forall eff. Eff eff Unit
foreign import unsetDrag :: forall eff. Eff eff Unit

netStream :: forall eff. Socket -> Eff( net :: Net | eff ) (Observable Action)
netStream socket = do
  onReceive <- socketObserver socket
  return $ actionFromForeign parseAction id <$> (parseMessage <$> onReceive)

dragStream :: forall eff. Eff( dom :: DOM| eff) (Observable Action)
dragStream = do
  lookup <- emitterLookup <$> getEmitters
  let dragOverSlot = (\e -> case parseSlot (getDetail e) of
                         Right s -> AssignTopic s
                         Left err -> UnassignTopic
                     ) <$> lookup "dragOverSlot"

      dragOverTrash = const DeleteTopic <$> lookup "dragOverTrash"

      -- HTML 5 fires dragLeave before dragEnd occurs
      -- TODO: Find a cleaner solution
      dragLeave = delay 30 $ const UnassignTopic
                  <$> lookup "dragLeaveSlot" `merge` lookup "dragLeaveTrash"

      dragOver = dragLeave `merge` dragOverSlot `merge` dragOverTrash

      dragStart = parseTopic <<< getDetail
                  <$> lookup "dragStartTopic" `merge` lookup "dragStartGridTopic"
      dragEnd = lookup "dragEndTopic" `merge` lookup "dragEndGridTopic"

      {- rebindable "bind" does make this a lot easier -}
      dragTopic :: Observable Action
      dragTopic = do
        let bind = flatMapLatest
        t' <- dragStart
        case t' of
          Right t -> do
            action <- dragOver
            dragEnd
            return (action t)
          Left e -> return $ ShowError (show e)

  runObservable $ const setDrag <$> dragStart
  runObservable $ const unsetDrag <$> dragEnd

  return dragTopic

uiStream :: forall eff. Eff( dom :: DOM | eff ) (Observable Action)
uiStream = do
  lookup <- emitterLookup <$> getEmitters
  let addTopic    = (actionFromForeign parseTopic AddTopic) <<< getDetail
                    <$> lookup "addTopic"
      addRoom     = (actionFromForeign parseRoom AddRoom) <<< getDetail
                    <$> lookup "addRoom"
      deleteRoom  = (actionFromForeign parseRoom DeleteRoom) <<< getDetail
                    <$> lookup "deleteRoom"
      addBlock    = (actionFromForeign parseBlock AddBlock) <<< getDetail
                    <$> lookup "addBlock"
      deleteBlock = (actionFromForeign parseBlock DeleteBlock) <<< getDetail
                    <$> lookup "deleteBlock"
      changeGrid = addRoom `merge` deleteRoom `merge` addBlock `merge` deleteBlock
  dragTopic <- dragStream
  return $ addTopic `merge` dragTopic `merge` changeGrid

main = do
  socketUrl <- getSocketUrl
  -- Uncomment for testing:
  let sockEmitter = getSocket ("ws://frost.kritzcreek.me/socket/0")
  -- Comment for testing:
  -- let sockEmitter = getSocket ("ws://" ++ socketUrl)

  -- Initial State
  appSt <- newSTRef emptyState
  -- Initial Render
  renderMenu (show <$> topicTypes)
  readSTRef appSt >>= renderApp

  --Request Initial State
  emitRefresh sockEmitter

  -- Observable Action
  ui  <- uiStream
  net <- netStream sockEmitter
  -- Broadcast the UI Observable
  subscribe ui (\a -> case a of
                   ShowError e -> log e
                   _ -> emitAction sockEmitter (serialize a))
  -- Evaluate Actions from the Server
  subscribe net (\a -> modifySTRef appSt (evalAction a) >>= renderApp)


Add initial Machine

Add initial Machine

This is a modification of the template instantiation machine from SPJ &
David Lester's Implementing Functional Languages. The main differences
are:
1. This is a closure machine - we don't compile to supercombinators
2. We always apply functions one argument at a time
3. We can evaluate inside of non-saturated lambdas

module Machine
  ( Machine(..)
  , start
  , smallStep
  , bigStep
  ) where

import Prelude

import Control.Monad.State (class MonadState, evalState, gets, modify_)
import Data.HashMap (HashMap)
import Data.HashMap as HashMap
import Data.List as List
import Data.List (List(..))
import Data.Maybe (Maybe(..))
import Data.Newtype (wrap)
import Data.Traversable (for)
import Data.TraversableWithIndex (forWithIndex)
import Data.Foldable (fold)
import Language.Expr (Expr(..))
import Language.Name (Name)
import Machine.Address (Address)
import Machine.Heap (Heap)
import Machine.Heap as Heap
import Machine.Node (Node(..), Stuck(..), Env)
import Machine.Stack (Stack)
import Machine.Stack as Stack
import Machine.Stash (Stash)
import Machine.Stash as Stash
import Machine.Trace (Trace(..))
import Partial.Unsafe (unsafeCrashWith)

type Machine =
  { root :: Address
  , stack :: Stack
  , stash :: Stash
  , heap :: Heap Node
  , globals :: HashMap Name Address
  , trace :: Trace
  }

initialize
  :: HashMap Name Expr
  -> Expr
  -> {root :: Address, globals :: HashMap Name Address, heap :: Heap Node }
initialize rawGlobals expr =
  flip evalState {heap: Heap.empty} do
    globals <- initializeGlobals rawGlobals
    root <- compile globals expr
    heap <- gets _.heap
    pure { root, globals, heap}

initializeGlobals
  :: forall s m
   . MonadState { heap :: Heap Node | s } m
  => HashMap Name Expr
  -> m (HashMap Name Address)
initializeGlobals globals = do
  env <- for globals \expr -> do
    addr <- Heap.alloc $ Pointer $ wrap 0
    pure { addr, expr }
  let addrs = _.addr <$> env
  forWithIndex env \name { addr, expr } -> do
    compiled <- compile addrs expr
    Heap.update addr $ Global name compiled
    pure addr

compile
  :: forall s m
   . MonadState { heap :: Heap Node | s } m
  => HashMap Name Address
  -> Expr
  -> m Address
compile globals = loop Nil
 where
  loop env0 = case _ of
    Bind name body -> Heap.alloc $ Closure env0 name body
    App f0 a0 -> do
      a <- loop env0 a0
      f <- loop env0 f0
      Heap.alloc $ Node f a
    Bound i ->
      pure $ deref i env0
    Free name ->
      case HashMap.lookup name globals of
        Nothing -> Heap.alloc $ Stuck $ StuckVar name
        Just addr -> pure addr

deref :: Int -> Env -> Address
deref i env = case List.index env i of
  Just a -> a
  Nothing -> unsafeCrashWith $ fold
    [ "Invalid De Bruijn index "
    , show i
    , " in environment "
    , show env
    ]

start :: HashMap Name Expr -> Expr -> Machine
start rawGlobals expr =
  { root
  , heap
  , globals
  , stack: Stack.singleton root
  , stash: Stash.empty
  , trace: Start
  }
 where
   {root, globals, heap} = initialize rawGlobals expr

emit :: forall s m. MonadState { trace :: Trace | s } m => Trace -> m Unit
emit message = modify_ _ { trace = message }

smallStep :: forall m. MonadState Machine m => m Unit
smallStep = do
  { top } <- gets _.stack
  eval top =<< Heap.fetch top

bigStep :: forall m. MonadState Machine m => m Unit
bigStep = do
  smallStep
  trace <- gets _.trace
  unless (interesting trace) bigStep
 where
  interesting = case _ of
    Fetched _ -> true
    Substituted _ _ -> true
    Halted _ -> true
    _ -> false

eval :: forall m. MonadState Machine m => Address -> Node -> m Unit
eval top = case _ of
  Node f _ -> do
    Stack.push f
    emit $ Unwound f

  Pointer address -> do
    Stack.peek 1 >>= case _ of
      Just app -> do
        arg <- fetchArg app
        Heap.update app $ Node address arg
      Nothing -> pure unit
    Stack.replace address
    emit $ Followed address

  Global name address -> do
    Stack.peek 1 >>= case _ of
      Just app -> do
        arg <- fetchArg app
        Heap.update app $ Node address arg
      Nothing -> Heap.update top $ Pointer address
    Stack.replace address
    emit $ Fetched name

  Closure env name e ->
    Stack.peek 1 >>= case _ of
      Just app -> do
        arg <- fetchArg app
        x <- instantiate (Cons arg env) e
        Heap.update app $ Pointer x
        Stack.discard
        Stack.replace x
        emit $ Substituted name arg
      Nothing -> do
        arg <- Heap.alloc $ Stuck $ StuckVar name
        x <- instantiate (Cons arg env) e
        p <- Heap.alloc $ Stuck $ StuckBind name x
        Heap.update top $ Pointer p
        Stack.replace x
        emit $ WentUnder top

  Stuck _ ->
    Stack.peek 1 >>= case _ of
      Just app -> do
        arg <- fetchArg app
        p <- Heap.alloc $ Stuck $ StuckApp top arg
        Heap.update app $ Pointer p
        Stack.discard
        Stack.replace arg
        Stash.suspend
        emit $ Discarded top arg
      Nothing ->
        Stash.restore >>= case _ of
          Just arg -> emit $ Discarded top arg
          Nothing -> emit $ Halted top

instantiate :: forall m. MonadState Machine m => Env -> Expr -> m Address
instantiate env0 = case _ of
  Bind name body ->
    Heap.alloc $ Closure env0 name body
  App f0 a0 -> do
    a <- instantiate env0 a0
    f <- instantiate env0 f0
    Heap.alloc $ Node f a
  Bound i -> do
    pure $ deref i env0
  Free name -> do
    globals <- gets _.globals
    case HashMap.lookup name globals of
      Nothing -> do
        stuck <- Heap.alloc $ Stuck $ StuckVar name
        modify_ _ { globals = HashMap.insert name stuck globals }
        pure stuck
      Just addr -> pure addr

fetchArg :: forall s m. MonadState { heap :: Heap Node | s } m => Address -> m Address
fetchArg address = do
  node <- Heap.fetch address
  case node of
    Node _ arg -> pure arg
    _ -> unsafeCrashWith $ fold
      [ "Non-application below top of stack: "
      , show node
      ]

error: pathspec 'src/Machine.purs' did not match any file(s) known to git


add Data.GetSpotsResponse

add Data.GetSpotsResponse

module Data.GetSpotsResponse where

import Data.Foreign.Class (class IsForeign, read, readProp)
import Data.Foreign.Index (prop)
import Data.Show (class Show, show)
import Prelude (($), (<>), bind, map, pure)

data GetSpotsItem = GetSpotsItem { id :: Int }
data GetSpotsResponse = GetSpotsResponse { ids :: Array GetSpotsItem }

instance getSpotsItemShow :: Show GetSpotsItem where
  show (GetSpotsItem { id }) = "(GetSpotsItem { id: " <> show id <> " })"

instance getSpotsItemIsForeign :: IsForeign GetSpotsItem where
  read value = do
    id <- readProp "id" value
    pure $ GetSpotsItem { id }

instance getSpotsResponseShow :: Show GetSpotsResponse where
  show (GetSpotsResponse { ids }) =
    "(GetSpotsResponse " <> show (map show ids) <> ")"

instance getSpotsResponseIsForeign :: IsForeign GetSpotsResponse where
  read value = do
    spotsForeign <- prop "spots" value
    ids <- read spotsForeign
    pure $ GetSpotsResponse { ids }

error: pathspec 'src/Data/GetSpotsResponse.purs' did not match any file(s) known to git


Add Codgen.JS

Add Codgen.JS

module Language.PureScript.CodeGen.JS
  ( ModuleType(..)
  , declToJs
  , moduleToJs
  ) where

import Control.Apply
import Control.Monad (replicateM)
import Data.Array
import Data.Either
import Data.Function (on)
import Data.Maybe
import Data.Maybe.Unsafe (fromJust)
import Data.Tuple
import Data.Tuple3
import Data.Tuple5
import Data.Traversable (for, traverse)
import qualified Data.Map as M

import Language.PureScript.CodeGen.Common
import Language.PureScript.CodeGen.JS.AST
import Language.PureScript.Declarations
import Language.PureScript.Environment
import Language.PureScript.Errors
import Language.PureScript.Names
import Language.PureScript.Options
-- import Language.PureScript.Optimizer
import Language.PureScript.Supply
import Language.PureScript.Traversals (sndM)
import Language.PureScript.Types

-- TODO: not this, once Language.PureScript.Optimizer is in place
foreign import optimize :: forall a. a

-- |
-- Different types of modules which are supported
--
data ModuleType = CommonJS | Globals

-- |
-- Generate code in the simplified Javascript intermediate representation for all declarations in a
-- module.
--
moduleToJs :: forall m. (Monad m) => ModuleType -> Options -> Module -> Environment -> SupplyT m [JS]
moduleToJs mt opts@(Options o) (Module name decls (Just exps)) env = do
  let jsImports = map (importToJs mt opts) $ (\x -> x \\ [name]) $ nub $ concatMap imports decls
  jsDecls <- traverse (\decl -> declToJs opts name decl env) decls
  let optimized = concat $ map (map $ optimize opts) $ catMaybes jsDecls
  let isModuleEmpty = null optimized
  let moduleBody = JSStringLiteral "use strict" : jsImports ++ optimized
  let moduleExports = JSObjectLiteral $ concatMap exportToJs exps
  return $ case mt of
    CommonJS -> moduleBody ++ [JSAssignment (JSAccessor "exports" (JSVar "module")) moduleExports]
    Globals | not isModuleEmpty ->
      [ JSVariableIntroduction (fromJust (o.optionsBrowserNamespace))
                               (Just (JSBinary Or (JSVar (fromJust (o.optionsBrowserNamespace))) (JSObjectLiteral [])) )
      , JSAssignment (JSAccessor (moduleNameToJs name) (JSVar (fromJust (o.optionsBrowserNamespace))))
                     (JSApp (JSFunction Nothing [] (JSBlock (moduleBody ++ [JSReturn moduleExports]))) [])
      ]
    _ -> []
moduleToJs _ _ _ _ = theImpossibleHappened "Exports should have been elaborated in name desugaring"

importToJs :: ModuleType -> Options -> ModuleName -> JS
importToJs mt (Options opts) mn = JSVariableIntroduction (moduleNameToJs mn) (Just moduleBody)
  where
  moduleBody = case mt of
    CommonJS -> JSApp (JSVar "require") [JSStringLiteral (runModuleName mn)]
    Globals -> JSAccessor (moduleNameToJs mn) (JSVar (fromJust (opts.optionsBrowserNamespace)))

imports :: Declaration -> [ModuleName]
imports =
  case everythingOnValues (++) (const []) collect (const []) (const []) (const []) of
    Tuple5 f _ _ _ _ -> f
  where
  collect :: Value -> [ModuleName]
  collect (Var (Qualified (Just mn) _)) = [mn]
  collect (Constructor (Qualified (Just mn) _)) = [mn]
  collect _ = []

-- |
-- Generate code in the simplified Javascript intermediate representation for a declaration
--
declToJs :: forall m. (Monad m) => Options -> ModuleName -> Declaration -> Environment -> SupplyT m (Maybe [JS])
declToJs opts mp (ValueDeclaration ident _ _ _ val) e = do
  js <- valueToJs opts mp e val
  return $ Just [JSVariableIntroduction (identToJs ident) (Just js)]
declToJs opts mp (BindingGroupDeclaration vals) e = do
  jss <- for vals $ \(Tuple3 ident _ val) -> do
    js <- valueToJs opts mp e val
    return $ JSVariableIntroduction (identToJs ident) (Just js)
  return $ Just jss
declToJs _ mp (DataDeclaration _ _ ctors) _ = do
  return $ Just $ flip concatMap ctors $ \(Tuple pn@(ProperName ctor) tys) ->
    [JSVariableIntroduction ctor (Just (go pn 0 tys []))]
    where
    go :: ProperName -> Number -> [Type] -> [JS] -> JS
    go pn _ [] values =
      JSObjectLiteral [ Tuple "ctor" (JSStringLiteral $ show $ Qualified (Just mp) pn)
                      , Tuple "values" (JSArrayLiteral $ reverse values) ]
    go pn index (_ : tys') values =
      JSFunction Nothing ["value" ++ show index]
        (JSBlock [JSReturn (go pn (index + 1) tys' (JSVar ("value" ++ show index) : values))])
declToJs opts mp (DataBindingGroupDeclaration ds) e = do
  jss <- traverse (\decl -> declToJs opts mp decl e) ds
  return $ Just $ concat $ catMaybes jss
declToJs _ _ (ExternDeclaration _ _ (Just js) _) _ = return $ Just [js]
declToJs opts mp (PositionedDeclaration _ d) e = declToJs opts mp d e
declToJs _ _ _ _ = return Nothing

-- |
-- Generate key//value pairs for an object literal exporting values from a module.
--
exportToJs :: DeclarationRef -> [Tuple String JS]
exportToJs (TypeRef _ (Just dctors)) = map (\(ProperName n) -> Tuple n (var (Ident n))) dctors
exportToJs (ValueRef name) = [Tuple (runIdent name) (var name)]
exportToJs (TypeInstanceRef name) = [Tuple (runIdent name) (var name)]
exportToJs _ = []

-- |
-- Generate code in the simplified Javascript intermediate representation for a variable based on a
-- PureScript identifier.
--
var :: Ident -> JS
var = JSVar <<< identToJs

-- |
-- Generate code in the simplified Javascript intermediate representation for an accessor based on
-- a PureScript identifier. If the name is not valid in Javascript (symbol based, reserved name) an
-- indexer is returned.
--
accessor :: Ident -> JS -> JS
accessor (Ident prop) = accessorString prop
accessor (Op op) = JSIndexer (JSStringLiteral op)

accessorString :: String -> JS -> JS
accessorString prop | isIdent prop = JSAccessor prop
accessorString prop                = JSIndexer (JSStringLiteral prop)

-- |
-- Generate code in the simplified Javascript intermediate representation for a value or expression.
--
valueToJs :: forall m. (Monad m) => Options -> ModuleName -> Environment -> Value -> SupplyT m JS
valueToJs _ _ _ (NumericLiteral n) = return $ JSNumericLiteral n
valueToJs _ _ _ (StringLiteral s) = return $ JSStringLiteral s
valueToJs _ _ _ (BooleanLiteral b) = return $ JSBooleanLiteral b
valueToJs opts m e (ArrayLiteral xs) = JSArrayLiteral <$> traverse (valueToJs opts m e) xs
valueToJs opts m e (ObjectLiteral ps) = JSObjectLiteral <$> traverse (sndM (valueToJs opts m e)) ps
valueToJs opts m e (ObjectUpdate o ps) = do
  obj <- valueToJs opts m e o
  sts <- traverse (sndM (valueToJs opts m e)) ps
  extendObj obj sts
valueToJs _ m _ (Constructor name) = return $ qualifiedToJS m (Ident <<< runProperName) name
valueToJs opts m e (Case values binders) = do
  vals <- traverse (valueToJs opts m e) values
  bindersToJs opts m e binders vals
valueToJs opts m e (IfThenElse cond th el) = JSConditional <$> valueToJs opts m e cond <*> valueToJs opts m e th <*> valueToJs opts m e el
valueToJs opts m e (Accessor prop val) = accessorString prop <$> valueToJs opts m e val
valueToJs opts m e (App val arg) = JSApp <$> valueToJs opts m e val <*> (return <$> valueToJs opts m e arg)
valueToJs opts m e (Let ds val) = do
  decls <- concat <<< catMaybes <$> traverse (flip (declToJs opts m) e) ds
  ret <- valueToJs opts m e val
  return $ JSApp (JSFunction Nothing [] (JSBlock (decls ++ [JSReturn ret]))) []
valueToJs opts m e (Abs (Left arg) val) = do
  ret <- valueToJs opts m (bindName m arg e) val
  return $ JSFunction Nothing [identToJs arg] (JSBlock [JSReturn ret])
valueToJs opts@(Options o) m e (TypedValue _ (Abs (Left arg) val) ty) | o.optionsPerformRuntimeTypeChecks = do
  let arg' = identToJs arg
  ret <- valueToJs opts m e val
  return $ JSFunction Nothing [arg'] (JSBlock $ runtimeTypeChecks arg' ty ++ [JSReturn ret])
valueToJs _ m _ (Var ident) = return $ varToJs m ident
valueToJs opts m e (TypedValue _ val _) = valueToJs opts m e val
valueToJs opts m e (PositionedValue _ val) = valueToJs opts m e val
valueToJs _ _ _ (TypeClassDictionary _ _ _) = theImpossibleHappened "Type class dictionary was not replaced"
valueToJs _ _ _ _ = theImpossibleHappened "Invalid argument to valueToJs"

-- |
-- Shallow copy an object.
--
extendObj :: forall m. (Monad m) => JS -> [Tuple String JS] -> SupplyT m JS
extendObj obj sts = do
  newObj <- freshName
  key <- freshName
  let
    jsKey = JSVar key
    jsNewObj = JSVar newObj
    block = JSBlock (objAssign:copy:extend ++ [JSReturn jsNewObj])
    objAssign = JSVariableIntroduction newObj (Just $ JSObjectLiteral [])
    copy = JSForIn key obj $ JSBlock [JSIfElse cond assign Nothing]
    cond = JSApp (JSAccessor "hasOwnProperty" obj) [jsKey]
    assign = JSBlock [JSAssignment (JSIndexer jsKey jsNewObj) (JSIndexer jsKey obj)]
    stToAssign (Tuple s js) = JSAssignment (JSAccessor s jsNewObj) js
    extend = map stToAssign sts
  return $ JSApp (JSFunction Nothing [] block) []

-- |
-- Temporarily extends the environment with a single local variable name
--
bindName :: ModuleName -> Ident -> Environment -> Environment
bindName m ident = bindNames m [ident]

-- |
-- Temporarily extends the environment to include local variable names introduced by lambda
-- abstractions or case statements
--
bindNames :: ModuleName -> [Ident] -> Environment -> Environment
bindNames m idents (Environment env) = Environment $ env {
    names = M.fromList (flip map idents (\ident -> Tuple (Tuple m ident) (Tuple noType LocalVariable))) `M.union` env.names
  }
  where
  noType = theImpossibleHappened "Temporary lambda variable type was read"

-- |
-- Generate code in the simplified Javascript intermediate representation for runtime type checks.
--
runtimeTypeChecks :: String -> Type -> [JS]
runtimeTypeChecks arg ty =
  let
    argTy = getFunctionArgumentType ty
  in
    maybe [] (argumentCheck (JSVar arg)) argTy
  where
  getFunctionArgumentType :: Type -> Maybe Type
  getFunctionArgumentType (TypeApp (TypeApp t funArg) _) | t == tyFunction = Just funArg
  getFunctionArgumentType (ForAll _ ty' _) = getFunctionArgumentType ty'
  getFunctionArgumentType _ = Nothing
  argumentCheck :: JS -> Type -> [JS]
  argumentCheck val t | t == tyNumber = [typeCheck val "number"]
  argumentCheck val t | t == tyString = [typeCheck val "string"]
  argumentCheck val t | t == tyBoolean = [typeCheck val "boolean"]
  argumentCheck val (TypeApp t _) | t == tyArray = [arrayCheck val]
  argumentCheck val (TypeApp t row) | t == tyObject =
    case rowToList row of
      Tuple pairs _ -> typeCheck val "object" : concatMap (\(Tuple prop ty') -> argumentCheck (accessorString prop val) ty') pairs
  argumentCheck val (TypeApp (TypeApp t _) _) | t == tyFunction = [typeCheck val "function"]
  argumentCheck val (ForAll _ ty' _) = argumentCheck val ty'
  argumentCheck _ _ = []
  typeCheck :: JS -> String -> JS
  typeCheck js ty' = JSIfElse (JSBinary NotEqualTo (JSTypeOf js) (JSStringLiteral ty')) (JSBlock [JSThrow (JSStringLiteral $ ty' ++ " expected")]) Nothing
  arrayCheck :: JS -> JS
  arrayCheck js = JSIfElse (JSUnary Not (JSApp (JSAccessor "isArray" (JSVar "Array")) [js])) (JSBlock [JSThrow (JSStringLiteral "Array expected")]) Nothing

-- |
-- Generate code in the simplified Javascript intermediate representation for a reference to a
-- variable.
--
varToJs :: ModuleName -> Qualified Ident -> JS
varToJs _ (Qualified Nothing ident) = var ident
varToJs m qual = qualifiedToJS m id qual

-- |
-- Generate code in the simplified Javascript intermediate representation for a reference to a
-- variable that may have a qualified name.
--
qualifiedToJS :: forall a. ModuleName -> (a -> Ident) -> Qualified a -> JS
qualifiedToJS m f (Qualified (Just m') a) | m /= m' = accessor (f a) (JSVar (moduleNameToJs m'))
qualifiedToJS _ f (Qualified _ a) = JSVar $ identToJs (f a)

-- |
-- Generate code in the simplified Javascript intermediate representation for pattern match binders
-- and guards.
--
bindersToJs :: forall m. (Monad m) => Options -> ModuleName -> Environment -> [CaseAlternative] -> [JS] -> SupplyT m JS
bindersToJs opts m e binders vals = do
  valNames <- replicateM (length vals) freshName
  let assignments = zipWith JSVariableIntroduction valNames (map Just vals)
  jss <- for binders $ \(CaseAlternative { binders = bs, guard = grd, result = result }) -> do
    ret <- valueToJs opts m (bindNames m (concatMap binderNames bs) e) result
    go valNames [JSReturn ret] bs grd
  return $ JSApp (JSFunction Nothing [] (JSBlock (assignments ++ concat jss ++ [JSThrow (JSStringLiteral "Failed pattern match")])))
                 []
  where
    go :: forall m. (Monad m) => [String] -> [JS] -> [Binder] -> Maybe Guard -> SupplyT m [JS]
    go _ done [] Nothing = return done
    go _ done [] (Just cond) = do
      cond' <- valueToJs opts m e cond
      return [JSIfElse cond' (JSBlock done) Nothing]
    go (v:vs) done' (b:bs) grd = do
      done'' <- go vs done' bs grd
      binderToJs m e v done'' b
    go _ _ _ _ = theImpossibleHappened "Invalid arguments to bindersToJs"

-- |
-- Generate code in the simplified Javascript intermediate representation for a pattern match
-- binder.
--
binderToJs :: forall m. (Monad m) => ModuleName -> Environment -> String -> [JS] -> Binder -> SupplyT m [JS]
binderToJs _ _ _ done NullBinder = return done
binderToJs _ _ varName done (StringBinder str) =
  return [JSIfElse (JSBinary EqualTo (JSVar varName) (JSStringLiteral str)) (JSBlock done) Nothing]
binderToJs _ _ varName done (NumberBinder num) =
  return [JSIfElse (JSBinary EqualTo (JSVar varName) (JSNumericLiteral num)) (JSBlock done) Nothing]
binderToJs _ _ varName done (BooleanBinder true) =
  return [JSIfElse (JSVar varName) (JSBlock done) Nothing]
binderToJs _ _ varName done (BooleanBinder false) =
  return [JSIfElse (JSUnary Not (JSVar varName)) (JSBlock done) Nothing]
binderToJs _ _ varName done (VarBinder ident) =
  return (JSVariableIntroduction (identToJs ident) (Just (JSVar varName)) : done)
binderToJs m e varName done (ConstructorBinder ctor bs) = do
  js <- go 0 done bs
  return
    if isOnlyConstructor e ctor
    then js
    else [ JSIfElse (JSBinary EqualTo (JSAccessor "ctor" (JSVar varName))
                                      (JSStringLiteral (show ctor)))
                    (JSBlock js)
                    Nothing
         ]
  where
  go :: forall m. (Monad m) => Number -> [JS] -> [Binder] -> SupplyT m [JS]
  go _ done' [] = return done'
  go index done' (binder:bs') = do
    argVar <- freshName
    done'' <- go (index + 1) done' bs'
    js <- binderToJs m e argVar done'' binder
    return (JSVariableIntroduction argVar (Just (JSIndexer (JSNumericLiteral index) (JSAccessor "values" (JSVar varName)))) : js)
binderToJs m e varName done (ObjectBinder bs) = go done bs
  where
  go :: forall m. (Monad m) => [JS] -> [Tuple String Binder] -> SupplyT m [JS]
  go done' [] = return done'
  go done' ((Tuple prop binder):bs') = do
    propVar <- freshName
    done'' <- go done' bs'
    js <- binderToJs m e propVar done'' binder
    return (JSVariableIntroduction propVar (Just (accessorString prop (JSVar varName))) : js)
binderToJs m e varName done (ArrayBinder bs) = do
  js <- go done 0 bs
  return [JSIfElse (JSBinary EqualTo (JSAccessor "length" (JSVar varName)) (JSNumericLiteral (length bs))) (JSBlock js) Nothing]
  where
  go :: forall m. (Monad m) => [JS] -> Number -> [Binder] -> SupplyT m [JS]
  go done' _ [] = return done'
  go done' index (binder:bs') = do
    elVar <- freshName
    done'' <- go done' (index + 1) bs'
    js <- binderToJs m e elVar done'' binder
    return (JSVariableIntroduction elVar (Just (JSIndexer (JSNumericLiteral index) (JSVar varName))) : js)
binderToJs m e varName done (ConsBinder headBinder tailBinder) = do
  headVar <- freshName
  tailVar <- freshName
  js1 <- binderToJs m e headVar done headBinder
  js2 <- binderToJs m e tailVar js1 tailBinder
  return [JSIfElse (JSBinary GreaterThan (JSAccessor "length" (JSVar varName)) (JSNumericLiteral 0)) (JSBlock
    ( JSVariableIntroduction headVar (Just (JSIndexer (JSNumericLiteral 0) (JSVar varName))) :
      JSVariableIntroduction tailVar (Just (JSApp (JSAccessor "slice" (JSVar varName)) [JSNumericLiteral 1])) :
      js2
    )) Nothing]
binderToJs m e varName done (NamedBinder ident binder) = do
  js <- binderToJs m e varName done binder
  return (JSVariableIntroduction (identToJs ident) (Just (JSVar varName)) : js)
binderToJs m e varName done (PositionedBinder _ binder) =
  binderToJs m e varName done binder

-- |
-- Checks whether a data constructor is the only constructor for that type, used to simplify the
-- check when generating code for binders.
--
isOnlyConstructor :: Environment -> Qualified ProperName -> Boolean
isOnlyConstructor (Environment e) ctor =
  let ty = maybe (theImpossibleHappened "Data constructor not found") id $ ctor `M.lookup` e.dataConstructors
  in numConstructors (Tuple ctor ty) == 1
  where
  numConstructors ty = length $ filter (((==) `on` typeConstructor) ty) $ M.toList $ e.dataConstructors
  typeConstructor (Tuple (Qualified (Just moduleName) _) (Tuple tyCtor _)) = Tuple moduleName tyCtor
  typeConstructor _ = theImpossibleHappened "Invalid argument to isOnlyConstructor"

error: pathspec 'src/Language/PureScript/CodeGen/JS.purs' did not match any file(s) known to git