haskell-nix / hnix

A Haskell re-implementation of the Nix expression language
https://hackage.haskell.org/package/hnix
BSD 3-Clause "New" or "Revised" License
753 stars 115 forks source link

evaluating simple nix expression with empty set has MVar deadlock #67

Closed ryantm closed 6 years ago

ryantm commented 6 years ago

Hi.

I really don't know what I'm doing with this library. I passed a NVSet into evalExpr with a pretty simple expression and got a MVar deadlock.

Here is the program:

module Main where

import Data.Fix
import Data.Map.Strict
import Nix.Eval
import Nix.Parser

main :: IO ()
main = do
  let Success nexpr = parseNixString "let r = t; in r"
  print nexpr
  let nval = Fix (NVSet (fromList []))
  evaled <- evalExpr nexpr nval
  print evaled

It outputs:

Fix (NLet [NamedVar [StaticKey "r"] (Fix (NSym "t"))] (Fix (NSym "r")))
nix-update: thread blocked indefinitely in an MVar operation

Thanks, Ryan

madjar commented 6 years ago

I spend quite some time thinking about this, and here is my current understanding. Please note that I'm not quite sure, and that it might be a bit unclear, but the partial understanding is still worth sharing.

The code for the evaluation for the let looks like this:

rec
  let mergedEnv = evaledBinds `Map.union` env
  evaledBinds <- evalBinds True mergedEnv binds

We use evalBinds to turn actually evaluate the bindings given an environment. But to allow to use one of the bindings in the definition of another, we tie the resulting value back into the input. When evaluated ed with Identity, this works, because, as long as we only have static bindings (ie no ${a} = 3), the keys can be know evaledBinds can be known, and everything work file.

So, evaluating let r = t; in r in Identity gives us Undefined variable: "t", which is good.

However, if we try to evaluate the same thing in Maybe, we get <<loop>>. I think this is because the bind of Maybe robs us from some of the lazyness: evalBinds need to evaluate every single value in the set in order to know whether the return value is Just or Nothing. And it cannot do that, because it needs its output to be already evaluated to normal form.

I fear something similar is happening in IO.

jwiegley commented 6 years ago

@ryantm Can you please try your program again? The code for doing this evaluation has changed a great deal in the past few weeks, and I believe this issue should be fixed now.