Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update renders typecheckedunisonfile without leading . #5427

Closed
aryairani opened this issue Oct 24, 2024 · 1 comment · Fixed by #5498
Closed

update renders typecheckedunisonfile without leading . #5427

aryairani opened this issue Oct 24, 2024 · 1 comment · Fixed by #5498
Assignees
Labels

Comments

@aryairani
Copy link
Contributor

Describe and demonstrate the bug
On this hash:
@unison/knn-search-demo/main>
⊙ 1. #htonjt947k

do an update with this scratch file,

db : '{Exception, Cloud} Database
db = do Database.named "shakespeare-hnsw2"


initializeMemoryGraph :
  '{IO, Exception} ( mutable.Ref {IO} (Optional (Layer, Play, Text)),
    mutable.Ref
    {IO} (data.Map Layer (data.Map (Play, Text) (Set (Play, Text)))))
initializeMemoryGraph = do
  use Debug trace
  use IO ref
  ep = ref None
  edges = ref data.Map.empty
  List.foreach
    (p -> let
      trace "loading to memory" p
      nodes = Http.run do streamPlayHunk p
      trace "nodes" (List.size nodes)
      nodes |> populatePlayGraph3 (memoryHNSW ep edges)) plays
  (ep, edges)


saveGraph : '{IO, Exception} ()
saveGraph = do
  Debug.trace "Initializing memory graph" ()
  (ep, edges) = initializeMemoryGraph()
  Debug.trace "done." ()
  Cloud.run do
    use Database named
    use mutable.Ref read
    clearDatabase()
    db = .db()
    Database.assign db Environment.default()
    ep' = read ep
    edges' = read edges
    copyMemoryToHNSW2 db ep' edges' (buildPlayGraph db)

and we get this in the resulting scratch file, which uses the wrong identifier db for .db

saveGraph : '{IO, Exception} ()
saveGraph = do
  use Debug trace
  use mutable.Ref read
  trace "Initializing memory graph" ()
  let
    (ep, edges) = initializeMemoryGraph()
    trace "done." ()
    Cloud.run do
      clearDatabase()
      db = db()
      Database.assign db Environment.default()
      ep' = read ep
      edges' = read edges
      copyMemoryToHNSW2 db ep' edges' (buildPlayGraph db)

Screenshots

Environment (please complete the following information):
release/0.5.27

Additional context

@mitchellwrosen
Copy link
Member

mitchellwrosen commented Dec 4, 2024

I believe there are two problems here.

Problem 1: no syntax for non-recursive let

Consider the following term (where bar is a variable).

foo = bar
foo

When parsed, it's represented as (roughly)

Let (Var "bar") (Abs "foo" (Var "foo"))

It looks a little inside-out, but it means:

  • Let the next binder be bound to variable "bar"
  • foo -> foo

All good so far. Now consider

foo = .foo
foo

Here the RHS of the binder refers to a "foo" defined elsewhere. It's similarly parsed as

Let (Var "foo") (Abs "foo" (Var "foo"))

or

  • Let the next binder be bound to variable "foo"
  • foo -> foo

Still all good so far. The problem, though, is that when we print this term with its non-recursive let, the syntax doesn't explicitly forbid self-reference. So we end up omitting

foo = foo
foo

which is parsed back as a busted self-reference that's not guarded by a lambda.

Problem 2: variable "resetting"

Consider now a similar situation, but involving a legitimate recursive let. It's a little contrived, but:

foo = do
  _ = foo -- to make foo recursive
  bar
foo()

This is parsed as

Cycle (
  Abs "foo" (
    LetRec 
      [ Lam (Abs Unit (Let (Var "foo") (Abs "_" (Var "bar")))) ]
      (App (Var "foo") Unit)
  )
)

Broken down, that's:

  • Introduce the variable "foo" to be in scope for an entire (one-element) cycle
  • Define the body of that binding as () -> let _ = foo in bar
  • Apply foo to ()

Now if we instead substitute .foo for bar, we'll get something similar:

Cycle (
  Abs "foo¹" (
    LetRec 
      [ Lam (Abs Unit (Let (Var "foo¹") (Abs "_" (Var "foo")))) ]
      (App (Var "foo¹") Unit)
  )
)

Or, back in familiar Unison syntax,

foo¹ = do
  _ = foo¹
  foo
foo¹()

where the binder "foo" has been freshened to "foo¹" so that it doesn't capture the internal free variable "foo".

Ok! but then the issue is that, when printing a term, we explicitly reset all variables with the following justification:

OK since all term vars are user specified, any freshening was just added during typechecking

This comment appears to be incorrect, as in this example we rely on freshening to disambiguate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants