Skip to content

Commit 1bc6ea3

Browse files
author
Your Name
committed
unification-fd based unifier:
chris@linux:~/Work/chrisdone/hell$ ./hell-status-quo --check /home/chris/Work/artificialio/brossa-infra/.github/workflows/scripts/summarise.hell --compiler-stats stat: read_file = 68.75 μs stat: parse_module_with_mode = 16.34 ms stat: resolve_module = 72.12 μs stat: parse = 16.60 ms stat: cycle_detect = 65.45 ms stat: desugar = 54.18 ms stat: elaborate = 1.590 ms stat: unify = 638.1 ms stat: zonk = 688.6 ms stat: infer = 1.328 s stat: check = 2.804 ms chris@linux:~/Work/chrisdone/hell$ ./hell-ufd --check /home/chris/Work/artificialio/brossa-infra/.github/workflows/scripts/summarise.hell --compiler-stats stat: read_file = 114.2 μs stat: parse_module_with_mode = 29.61 ms stat: resolve_module = 79.21 μs stat: parse = 30.09 ms stat: cycle_detect = 63.70 ms stat: desugar = 62.56 ms stat: elaborate = 3.719 ms stat: unify-and-zonk = 270.5 ms stat: infer = 274.4 ms stat: check = 2.561 ms
1 parent 4604cf4 commit 1bc6ea3

3 files changed

Lines changed: 163 additions & 8 deletions

File tree

hell.cabal

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ executable hell
3030
, containers
3131
, criterion
3232
, criterion-measurement
33+
, data-fix
3334
, directory
3435
, ghc-prim
3536
, haskell-src-exts
@@ -46,6 +47,7 @@ executable hell
4647
, th-orphans
4748
, these
4849
, time
50+
, transformers
4951
, typed-process
5052
, unification-fd
5153
, unliftio
@@ -71,6 +73,7 @@ test-suite hell-test
7173
, containers
7274
, criterion
7375
, criterion-measurement
76+
, data-fix
7477
, directory
7578
, ghc-prim
7679
, haskell-src-exts
@@ -87,6 +90,7 @@ test-suite hell-test
8790
, th-orphans
8891
, these
8992
, time
93+
, transformers
9094
, typed-process
9195
, unification-fd
9296
, unliftio

package.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ dependencies:
3535
- warp
3636
- wai
3737
- unification-fd
38+
- transformers
39+
- data-fix
3840
- http-types
3941
- case-insensitive
4042

src/Hell.hs

Lines changed: 157 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE DeriveAnyClass #-}
12
{-# LANGUAGE AllowAmbiguousTypes #-}
23
{-# LANGUAGE BangPatterns #-}
34
{-# LANGUAGE BlockArguments #-}
@@ -42,8 +43,14 @@
4243

4344
module Main (main, specMain) where
4445

46+
4547
#if __GLASGOW_HASKELL__ >= 906
48+
import Data.Tuple
49+
import Data.Fix
50+
import qualified Control.Unification.Types as FD
51+
import Control.Monad.Trans.Except
4652
import qualified Control.Unification as FD
53+
import qualified Control.Unification.STVar as FD
4754
import GHC.Generics (Generic1)
4855
import Control.Monad
4956
#endif
@@ -52,6 +59,7 @@ import Control.Monad
5259
-- e.g. 'Data.Graph' becomes 'Graph', and are then exposed to the Hell
5360
-- guest language as such.
5461

62+
import Control.Monad.ST
5563
import qualified Data.CaseInsensitive as CI
5664
import Data.CaseInsensitive (CI, FoldCase)
5765
import qualified Network.HTTP.Types as Http
@@ -211,7 +219,7 @@ compileFile stats filePath = do
211219
case lookup "main" dterms of
212220
Nothing -> error "No main declaration!"
213221
Just main' -> do
214-
inferred <- inferExp (nestStat stats) main'
222+
inferred <- infer_exp (nestStat stats) main'
215223
case inferred of
216224
Left err -> error $ prettyString err
217225
Right uterm -> do
@@ -1636,7 +1644,10 @@ data InferError
16361644
= UnifyError UnifyError
16371645
| ZonkError ZonkError
16381646
| ElabError ElaborateError
1639-
deriving (Show)
1647+
| ST_mapping_error
1648+
| ST_unify_error
1649+
| ST_apply_bindings_error
1650+
deriving instance Show InferError
16401651

16411652
-- | Note: All types in the input are free of metavars. There is an
16421653
-- intermediate phase in which there are metavars, but then they're
@@ -1665,6 +1676,19 @@ inferExp stats uterm = do
16651676
emitStat stats "zonk" (t3 - t2)
16661677
pure $ Right sterm
16671678

1679+
infer_exp ::
1680+
StatsEnabled ->
1681+
UTerm () ->
1682+
IO (Either InferError (UTerm SomeTypeRep))
1683+
infer_exp stats uterm = do
1684+
t0 <- getTime
1685+
case elaborate uterm of
1686+
Left elabError -> pure $ Left $ ElabError elabError
1687+
Right (iterm, equalities) -> do
1688+
t1 <- getTime
1689+
emitStat stats "elaborate" (t1 - t0)
1690+
st_unify stats equalities iterm
1691+
16681692
-- | Zonk a type and then convert it to a type: t :: *
16691693
zonkToStarType :: Map IMetaVar (IRep IMetaVar) -> IRep IMetaVar -> Either ZonkError SomeTypeRep
16701694
zonkToStarType subs irep = do
@@ -2646,6 +2670,103 @@ data Ty a
26462670
| TyFun a a
26472671
| TyCon SomeTypeRep
26482672
deriving (Functor, Traversable, Foldable, Eq, Ord, Show, Generic1)
2673+
-- Below: Needed for FD.freeVar (note for myself, not audience)
2674+
deriving instance FD.Unifiable Ty
2675+
2676+
-- WIP: At this point we want, preferably, a function that goes
2677+
-- from an (IRep IMetaVar) to SomeTypeRep in one go, which inferExp can
2678+
-- use to traverse f iterm to get UTerm SomeTypeRep. Or it receives the expr
2679+
-- to traverse over, due to the ST monadness.
2680+
--
2681+
-- Some key ingredients:
2682+
--
2683+
-- to_sometyperep :: Fix Ty -> Either ZonkError SomeTypeRep
2684+
-- irep_to_uterm :: IRep v -> FD.UTerm Ty v
2685+
-- FD.applyBindings :: UTerm t v -> em m (UTerm t v)
2686+
--
2687+
-- I'd say we want to:
2688+
--
2689+
-- 1) Convert the IRep IMetaVar to UTerm t v using the (Map IMetaVar (STVar ..)) map.
2690+
-- 2) Apply bindings to fully flesh out the type.
2691+
-- 3) Zonk it, going directly from FD.UTerm Ty straight to SomeTypeRep
2692+
-- (tweak to_sometyperep to work with UTerm Ty rather than Fix Ty)
2693+
--
2694+
-- That should be all that's needed?
2695+
2696+
-- Unify all the constraints in @equalities@, zonk and update the term and return it.
2697+
st_unify :: Traversable t
2698+
=> StatsEnabled
2699+
-> Set (Equality (IRep IMetaVar))
2700+
-> t (IRep IMetaVar)
2701+
-> IO (Either InferError (t SomeTypeRep))
2702+
st_unify stats set term = do
2703+
t1 <- getTime
2704+
let result =
2705+
FD.runSTBinding do
2706+
-- Thaw the equalities.
2707+
(equalities, mapping) <- run_stize $ stize_equalities $ Set.toList set
2708+
-- Unify the mutable equalities into the ambient environment.
2709+
result <- runExceptT $ st_unifier_ equalities
2710+
case result of
2711+
Left err -> pure $ Left ST_unify_error
2712+
Right () -> do
2713+
-- Apply bindings, zonk and freeze all the types across the term.
2714+
runExceptT $
2715+
for term \irepmv -> do
2716+
uterm <- for (irep_to_uterm irepmv) \imv ->
2717+
case Map.lookup imv mapping of
2718+
Nothing -> throwE $ ST_mapping_error
2719+
Just stvar -> pure stvar
2720+
uterm' <- withExceptT (const ST_apply_bindings_error) $ st_apply uterm
2721+
except $ first ZonkError $
2722+
to_sometyperep
2723+
mapping
2724+
uterm'
2725+
case result of
2726+
Left err -> pure $ Left err
2727+
Right (!term') -> do
2728+
t2 <- getTime
2729+
emitStat stats "unify-and-zonk" (t2 - t1)
2730+
pure $ Right term'
2731+
2732+
-- Apply bindings; this type signature avoids type inference problems.
2733+
st_apply :: FD.UTerm Ty (FD.STVar s Ty)
2734+
-> ExceptT (FD.UFailure Ty (FD.STVar s Ty)) (FD.STBinding s) (FD.UTerm Ty (FD.STVar s Ty))
2735+
st_apply = FD.applyBindings
2736+
2737+
-- Unify all the equality constraints provided.
2738+
st_unifier_ :: [Equality (FD.UTerm Ty (FD.STVar s Ty))]
2739+
-> ExceptT (FD.UFailure Ty (FD.STVar s Ty)) (FD.STBinding s) ()
2740+
st_unifier_ = traverse_ \(Equality src a b) -> void $ FD.unify a b
2741+
2742+
-- Run an ST-izing operation, and return the mapping from the original IMetaVars to the STVars, so that they can be recovered later.
2743+
run_stize :: StateT (Map IMetaVar (FD.STVar s Ty)) (FD.STBinding s) x -> FD.STBinding s (x, Map IMetaVar (FD.STVar s Ty))
2744+
run_stize = flip runStateT (mempty :: Map IMetaVar (FD.STVar s Ty))
2745+
2746+
-- ST-ize the metavars in all types in the equality constraints set
2747+
stize_equalities :: [Equality (IRep IMetaVar)] -> StateT (Map IMetaVar (FD.STVar s Ty)) (FD.STBinding s) [Equality (FD.UTerm Ty (FD.STVar s Ty))]
2748+
stize_equalities = traverse stize_equality
2749+
2750+
-- ST-ize the metavars in all types in the equality's types on both sides, it also
2751+
-- is exactly where conversion from IRep to UTerm Ty occurs.
2752+
stize_equality :: Equality (IRep IMetaVar) -> StateT (Map IMetaVar (FD.STVar s Ty)) (FD.STBinding s) (Equality (FD.UTerm Ty (FD.STVar s Ty)))
2753+
stize_equality (Equality loc a b) = do
2754+
Equality loc <$> stize_uterm (irep_to_uterm a) <*> stize_uterm (irep_to_uterm b)
2755+
2756+
-- ST-ize the metavars in a uterm
2757+
stize_uterm :: FD.UTerm Ty IMetaVar -> StateT (Map IMetaVar (FD.STVar s Ty)) (FD.STBinding s) (FD.UTerm Ty (FD.STVar s Ty))
2758+
stize_uterm = traverse stize_imetavar
2759+
2760+
-- ST-ize this metavar and remember that it was done
2761+
stize_imetavar :: IMetaVar -> StateT (Map IMetaVar (FD.STVar s Ty)) (FD.STBinding s) (FD.STVar s Ty)
2762+
stize_imetavar = \v -> do
2763+
mexisting <- gets (Map.lookup v)
2764+
case mexisting of
2765+
Just stv -> pure stv
2766+
Nothing -> do
2767+
stv <- lift FD.freeVar
2768+
modify' (Map.insert v stv)
2769+
pure stv
26492770

26502771
-- <bijection>
26512772
irep_to_uterm :: IRep v -> FD.UTerm Ty v
@@ -2654,14 +2775,36 @@ irep_to_uterm = \case
26542775
IApp f x -> FD.UTerm (TyApp (irep_to_uterm f) (irep_to_uterm x))
26552776
IFun f x -> FD.UTerm (TyFun (irep_to_uterm f) (irep_to_uterm x))
26562777
ICon t -> FD.UTerm $ TyCon t
2657-
uterm_to_irep :: FD.UTerm Ty v -> IRep v
2658-
uterm_to_irep = \case
2659-
FD.UVar v -> IVar v
2660-
FD.UTerm (TyApp f x) -> IApp (uterm_to_irep f) (uterm_to_irep x)
2661-
FD.UTerm (TyFun f x) -> IFun (uterm_to_irep f) (uterm_to_irep x)
2662-
FD.UTerm (TyCon t) -> ICon t
26632778
-- </bijection>
26642779

2780+
-- | (unification-fd edition)
2781+
-- A complete implementation of conversion from the inferer's type
2782+
-- rep to some star type, ready for the type checker.
2783+
-- Jumps straight from UTerm Ty to SomeTypeRep in one go; handles
2784+
-- ambiguous vars and kind errors here.
2785+
to_sometyperep :: forall v. Eq v => (Map IMetaVar v) ->
2786+
FD.UTerm Ty v -> Either ZonkError SomeTypeRep
2787+
to_sometyperep mapping t = do
2788+
go t
2789+
where
2790+
go :: FD.UTerm Ty v -> Either ZonkError SomeTypeRep
2791+
go = \case
2792+
FD.UVar k -> Left $ ST_ambiguous_var (List.lookup k $ map swap $ Map.toList mapping)
2793+
FD.UTerm (TyCon someTypeRep) -> pure someTypeRep
2794+
FD.UTerm (TyFun a b) -> do
2795+
a' <- go a
2796+
b' <- go b
2797+
case (a', b') of
2798+
(StarTypeRep aRep, StarTypeRep bRep) ->
2799+
pure $ StarTypeRep (Type.Fun aRep bRep)
2800+
_ -> Left ZonkKindError
2801+
FD.UTerm (TyApp f a) -> do
2802+
f' <- go f
2803+
a' <- go a
2804+
case applyTypes f' a' of
2805+
Just someTypeRep -> pure someTypeRep
2806+
_ -> Left ZonkKindError
2807+
26652808
--------------------------------------------------------------------------------
26662809
-- Inference type representation
26672810

@@ -2675,6 +2818,7 @@ data IRep v
26752818
data ZonkError
26762819
= ZonkKindError
26772820
| AmbiguousMetavar IMetaVar
2821+
| ST_ambiguous_var (Maybe IMetaVar)
26782822
deriving (Show)
26792823

26802824
-- | A complete implementation of conversion from the inferer's type
@@ -3109,6 +3253,11 @@ instance (Pretty a) => Pretty (IRep a) where
31093253
instance Pretty ZonkError where
31103254
pretty = \case
31113255
ZonkKindError -> "Kind error."
3256+
ST_ambiguous_var imetavar -> "(ST) Ambiguous meta variable: "
3257+
<> maybe "???" pretty imetavar
3258+
<> "\n"
3259+
<> "arising from "
3260+
<> maybe "???" (pretty . (.srcSpanInfo)) imetavar
31123261
AmbiguousMetavar imetavar ->
31133262
"Ambiguous meta variable: "
31143263
<> pretty imetavar

0 commit comments

Comments
 (0)