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
751 stars 115 forks source link

Finish a QuasiQuoter for Nix expressions #52

Open jwiegley opened 7 years ago

jwiegley commented 7 years ago

The basic quasi-quoter is in place now, just import Nix.TH, turn on QuasiQuotes and TemplateHaskell, and then use [nix| 2 + 3|], which will parse that text into a value of type NExpr, exactly as parseNixString would.

There are a few next steps for this work to make it more useful:

jwiegley commented 6 years ago

This is really just: findBinding (StaticKey "a") (parseNixString "inherit a b c;"). Where a quasi-quater would be become interesting is in anti-quoting Haskell variables correctly based on their type.

jwiegley commented 6 years ago

The basic quasi-quoter is in place now, just import Nix.TH, turn on QuasiQuotes and TemplateHaskell, and then use [nix| 2 + 3|], which will parse that text into a value of type NExpr, exactly as parseNixString would.

There are a few next steps for this work to make it more useful:

jwiegley commented 6 years ago

We now support capturing of Haskell bindings, and Nix expression patterns:

foo :: ToExpr a -> a -> NExprLoc
foo x = [nix| x + 100|]

bar :: NExprLoc -> NExprLoc
bar [nix| x + 200 |] = x
hamishmack commented 5 years ago

What it the state of Nix.TH? I am having trouble using it. The error I keep getting is:

[nix-shell:~/iohk/nix-tools]$ ghci -package hnix -XQuasiQuotes -XTemplateHaskell
GHCi, version 8.6.5: http://www.haskell.org/ghc/  :? for help
Prelude> import Nix.TH 
Prelude Nix.TH> [nix| x |]

<interactive>:2:6: error:
    • Can't find interface-file declaration for variable Data.Text.Internal.pack
        Probable cause: bug in .hi-boot file, or inconsistent .hi file
        Use -ddump-if-trace to get an idea of which file caused the error
    • In the first argument of ‘Nix.Expr.Types.NSym’, namely
        ‘(Data.Text.Internal.pack (((:) 'x') []))’
      In the first argument of ‘Data.Fix.Fix’, namely
        ‘(Nix.Expr.Types.NSym (Data.Text.Internal.pack (((:) 'x') [])))’
      In the expression:
        (Data.Fix.Fix
           (Nix.Expr.Types.NSym (Data.Text.Internal.pack (((:) 'x') []))))
jwiegley commented 5 years ago

Sounds like a bug that will need further examination.

CMCDragonkai commented 4 years ago

What's the status of the bug?

sjakobi commented 4 years ago

I can reproduce the bug in cabal repl on Linux. https://gitlab.haskell.org/ghc/ghc/issues/12596 looks similar.

Anton-Latukha commented 3 years ago

Somewhat related to:

Allow Nix functions to be evaluated into native Haskell functions. Thus, we could do this: `let f = nixFunction "my-function.nix" in [nix| $((f 1)) + 2|]. The intention here is to make the transition between Haskell and Nix entirely seamless, while preserving lazy semantics as closely as possible to the expectations of both languages.

In Conversion there is now inHask* (best name I came up so far for) methods, methods are isomorphisms between simple Haskell (which for simplicity I call Hask category) & embedded into it Nix category. It is not total isomorphism, meaning it is isomorphism transformatons that currently work on particular types.

A lot of Conversion module is really an isomorphism that can be denoted & leveraged through more default Haskell means of defining isomorphisms. inHask* functions is Nix -> computation in Haskell -> Nix. What is asked in the head post is a Nix function application inside Haskell, automatic Nix function -> Haskell function transformation. Currently it seems too early, since it would blur the line between Haskell & Nix & can complicate further work on the project code, when Haskell pathing suddenly dissapers inside .nix. We also probably need a solid language symantic checer & type checker to load .nix files, that is why it seems that it needs to be done at a pretty late stage when the HNix code would be solid & clear enough to withstand that blurring (it is just my current view on it).

Anton-Latukha commented 2 years ago

https://github.com/haskell-nix/hnix/issues/52#issuecomment-492074339 <- now works

Cleaning up the thread accordingly.

Anton-Latukha commented 2 years ago

Main quasiquoter mode works, and updated the type system, so it looks fancier:

-- add QuasiQuotes & TemplateHaskell into scope
λ> [nix| { a }: let v = "now"; in rec { fancy = ''Hey, ${v}!''; ver = builtins.languageVersion; } |]
Fix (NAbs (ParamSet Nothing Closed [(VarName "a",Nothing)]) (Fix (NLet [NamedVar (StaticKey (VarName "v") :| []) (Fix (NStr (DoubleQuoted [Plain "now"]))) (SourcePos {sourceName = "<string>", sourceLine = Pos 1, sourceColumn = Pos 13})] (Fix (NSet Recursive [NamedVar (StaticKey (VarName "fancy") :| []) (Fix (NStr (Indented 0 [Plain "Hey, ",Antiquoted (Fix (NSym (VarName "v"))),Plain "!"]))) (SourcePos {sourceName = "<string>", sourceLine = Pos 1, sourceColumn = Pos 33}),NamedVar (StaticKey (VarName "ver") :| []) (Fix (NSelect Nothing (Fix (NSym (VarName "builtins"))) (StaticKey (VarName "languageVersion") :| []))) (SourcePos {sourceName = "<string>", sourceLine = Pos 1, sourceColumn = Pos 57})])))))

Allow Haskell functions to be anti-quoted

Useful bits for this is in: https://github.com/mikeplus64/QuasiText/blob/master/src/Text/QuasiText.hs (as $( )).

Quasiquoter pattern matching got broken during migration to Text. I was checking the main way, but not checked the pattern match way. Pattern match tries to launch pack and searches for it in the wrong module (pack is in Data.Text):

λ> bar [nix| x + 200 |] = x

<interactive>:13:5: error:
    • Exception when trying to run compile-time code:
        Can't construct a pattern from name Data.Text.Internal.pack
CallStack (from HasCallStack):
  error, called at libraries/template-haskell/Language/Haskell/TH/Syntax.hs:1300:22 in template-haskell:Language.Haskell.TH.Syntax
      Code: Language.Haskell.TH.Quote.quotePat nix " x + 200 "
    • In the quasi-quotation: [nix| x + 200 |]

GHC upstream: https://gitlab.haskell.org/ghc/ghc/-/issues/12596 Workaround: https://stackoverflow.com/questions/38143464/cant-find-inerface-file-declaration-for-variable - but that needs to be ported to PatQ & so far I have not found how to do it (I am currently not well-versed in TemplateHaskell metaprogramming dimension).

Gabriella439 commented 2 years ago

The support for free variables appears to not be working in hnix-0.14.0.5, unless I'm doing something wrong:

>>> x = mkInt 1
>>> [nix|x|]
Fix (NSym "x")

I also tested in a standalone file outside of the REPL, too, with the same result.

Anton-Latukha commented 2 years ago

Currently main HNix branch does:

λ> x = mkInt 1
x :: Nix.Expr.Types.NExpr
λ> [nix|x|]
Fix (NSym (VarName "x"))

Which is a step in the right direction.

You are right, the test should be there all along.

Anton-Latukha commented 2 years ago

Gabriella, I am sorry, it is probable that it is me who broke this feature.

Can you detail the reason/priority of use, so I can infer things?

As I still have difficulty in metaprogramming on the TH level- it probably would be a task that at least take a chunk of time of learning & troubleshooting this.

Gabriella439 commented 2 years ago

Not a high priority for us

Anton-Latukha commented 2 years ago

Thank you. But I would know I need to address it, as I am probably who broke it.

P.S.

Overall, the undocumented breakages in Nix release 2.3.15 & 2.4 & 2.5 put me & the project in a hard place. That upstream breaks without notification/documentation were addressed by public action in upstream by maintainers, but that does not resolves the situation really.