Repository: unisonweb/unison

Issue #5025: typechecker crash on omitted ability set
Created: 2024-05-30T13:07:52Z
This is a fairly simple Stream.find implementation:
find :  (a -> {g} Boolean) -> '{g, Stream a} r -> Optional a
find f s =
  handle !s with cases
    { _ } -> None
    { emit a -> k } ->
      if f a then Some a else find f k

It results in the following:
  Sorry, you hit an error we didn't make a nice message for yet.

  Here is a summary of the Note:

    simple cause:
      TypeMismatch
    context:
    Γ
      |start1|
      |let-rec-marker2|
      |𝕩4|
      'Inference Ability-5
      'Inference Ability-77
      '𝕖78 = 𝕖5
      'User "g"-79
      '𝕖6 = 𝕖77, 𝕖5, g79
      find3 : (a ->{g} Boolean) ->{𝕖5} (Unit ->{g, Stream a} r) ->{𝕖77, 𝕖5, g79} Optional a
      |a8|
      @a9
      |g11|
      @g12
      |r14|
      @r15
      |a17|
      @a18
      |g20|
      @g21
      |r23|
      @r24
      |f26|
      f25 : a18 ->{g21} Boolean
      |s28|
      s27 : Unit ->{g21, Stream a18} r24
      '𝕧38 = r24
      'Inference Ability-41
      'e44 = a18
      '𝕗43 = Stream
      '𝕖42 = Stream a18
      '𝕦40 = 𝕖41, Stream a18
      '𝕗39 = Request
      '𝕗37 = Request {𝕖41, Stream a18}
      'vn6c9r3215130 = Request {𝕖41, Stream a18} r24
      '𝕖72 =
      '𝕖73 = 𝕖77, 𝕖5, g79
      '𝕖74 = 𝕖5
      'g75 = g21
      '𝕖31 = 𝕖77, 𝕖5, g79, 𝕖5, g21
      'a51 = a18
      '𝕗50 = Optional
      '𝕣32 = Optional a18
      vn6c9r3215129 : Request {𝕖41, Stream a18} r24
      'e33 = a18
      '𝕖35 = 𝕖41
      '𝕧34 = r24
      'Inference Ability-36
      'e71 = a18
      'Inference Ability-76

Simply adding a {g} in front of the result type avoids the crash, and the function works as expected:
find :  (a -> {g} Boolean) -> '{g, Stream a} r -> {g} Optional a
find f s =
  handle !s with cases
    { _ } -> None
    { emit a -> k } ->
      if f a then Some a else find f k

Duplicate issue #5629: No nice message for pattern match error
Created: 2025-03-16T22:06:51Z
Version: release/0.5.36
Error:
grain/main> 

  Loading changes detected in ~/workspace-unison/scratch-dev/grain.u.


  Sorry, you hit an error we didn't make a nice message for yet.
  
  Here is a summary of the Note:
  
    simple cause:
      TypeMismatch
    context:
    Γ
      |start1|
      Grain.ref13 : Text -> GrainSpec m5 -> GrainRef m5
      Grain.send16 : m15 -> GrainRef m15 ->{GrainContext} Unit
      Grain.spec37 : s23 -> (Unit ->{Grain m20, Store s23, GrainContext} Unit) -> GrainSpec m20
      GrainSpec.behavior95 : GrainSpec m41 -> Unit ->{Grain m41, Store s44, GrainContext} Unit
      GrainSpec.initialState145 : GrainSpec m99 -> s102
      |𝕩146|
      |effect148|
      effect147 : Unit ->{GrainContext} Unit
      |𝕩149|
      'User "\120150"-150
      'User "v"-158
      'User "k"-160
      '𝕗159 = Map
      '𝕗157 = Map k160
      'a151 = Map k160 v158
      'User "g"-152
      'User "v"-153
      'k154 = k160
      'v155 = v158
      'Inference Ability-156
      |()162|
      ()161 : Unit
      'Inference PatternPureV-172
      'Inference Ability-175
      '𝕖176 = GrainContext
      '𝕦174 = 𝕖175, GrainContext
      '𝕗173 = Request
      '𝕗171 = Request {𝕖175, GrainContext}
      'j4s9cq5p0k1164 = Request {𝕖175, GrainContext} 𝕧172
      'Inference Ability-290
      'User "m"-295
      '𝕗294 = Grain
      '𝕖291 = Grain m295
      'Inference Ability-292
      'Inference Ability-293
      '𝕖165 = 𝕖290, Grain m295, 𝕖292, 𝕖293
      'Inference Output-166
      j4s9cq5p0k1163 : Request {𝕖175, GrainContext} 𝕧172
      'User "m"-167
      '𝕖169 = 𝕖175
      '𝕧168 = 𝕧172
      'Inference Ability-170
      'User "v"-194
      'User "m"-286
      'm287 = m286
      'm288 = m286
      'f289 = _GrainSpec m286

Scratch file:

ability Grain m where
  receive : m

ability GrainContext where 
  _send : m -> GrainRef m -> ()

type _GrainSpec m s = _GrainSpec s ('{Grain m, Store s, GrainContext} ())

type GrainSpec m = GrainSpec (Exists (_GrainSpec m))

GrainSpec.initialState : GrainSpec m -> s
GrainSpec.initialState = cases
  GrainSpec impl -> 
    impl |> Exists.apply cases _GrainSpec s _ -> Any s |> unsafeExtract

GrainSpec.behavior : GrainSpec m -> ('{Grain m, Store s, GrainContext} ())
GrainSpec.behavior = cases
  GrainSpec impl -> 
    impl |> Exists.apply cases _GrainSpec _ b -> Any b |> unsafeExtract


type GrainRef m = GrainRef Text (GrainSpec m)

Grain.send = GrainContext._send

Grain.spec : s -> ('{Grain m, Store s, GrainContext} ()) -> GrainSpec m
Grain.spec initialState behavior = 
  GrainSpec (Exists.wrap (_GrainSpec initialState behavior))

Grain.ref : Text -> GrainSpec m -> GrainRef m
Grain.ref id spec = GrainRef id spec

type PlayerMessage = ;

type Player = Player (GrainRef PlayerMessage)

Player.ref : Text -> Player
Player.ref name = 
  ref = 
    Grain.ref
      name
      (Grain.spec 0 do
        match Grain.receive with
          _ -> ()
        ()
      )
  Player ref

type RoomMessage = Join Player | GetPlayers ([Player] ->{GrainContext} ())

type Room = Room (GrainRef RoomMessage)

Room.ref : Text -> Room
Room.ref name = 
  ref = 
    Grain.ref
      name
      (Grain.spec [] do
        match Grain.receive with
          RoomMessage.Join player -> 
            Store.modify ((List.+:) player)
          GetPlayers callback ->
            callback Store.get
        ()
      )
  Room ref

Room.join : Player -> Room -> ()
Room.join player room = 
  (Room ref) = room 
  ref |> Grain.send (RoomMessage.Join player)

Room.getPlayers : ([Player] ->{GrainContext} ()) -> Room -> ()
Room.getPlayers callback room = 
  (Room ref) = room
  ref |> Grain.send (GetPlayers callback)


main = do
  Grain.run do 
    player1 = Player.ref "alvaro"
    player2 = Player.ref "alex"
    player3 = Player.ref "adam"

    room = Room.ref "room1"

    room |> join player1
    room |> join player2
    room |> join player3

    room |> getPlayers (trace "players")

    ()

type GrainThread = GrainThread Text Any

Grain.run : '{GrainContext} () ->{} ()
Grain.run effect = 

  Store.withInitialValue Map.empty do
    handle effect() with cases
      { GrainContext._send msg (GrainRef grainId spec) -> k } -> 
        grainOpt = Store.get |> Map.get grainId 

        match grainOpt with
          None -> 
            initialState = GrainSpec.initialState spec
            behavior = GrainSpec.behavior spec

            Store.withInitialValue initialState do
              behavior()

            Debug.trace "initialState" initialState
            Debug.trace "behavior" behavior
            thread = GrainThread grainId
            ()
          Some grain -> ()

        trace "grainOpt" grainOpt
        todo "context"
      { _ } -> 
        todo "end"

  ()

Closing comment (2025-03-28T19:38:27Z): This might be a duplicate of #5025. See if you can figure out whether the solution there applies to your case (e.g., if there is a missing ability somewhere).