Dhall-haskell: Add `dhall freeze` subcommand

Created on 7 Jun 2018  路  12Comments  路  Source: dhall-lang/dhall-haskell

It would be nice if users could more easily take advantage of semantic integrity checks by automatically pinning all imports in a file. For example, if a user had the following file:

-- ./example.dhall

    let replicate =
          https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/List/replicate

in  replicate 5

... then they could pipe that through dhall freeze to pin the replicate import with a semantic integrity check:

$ dhall freeze < ./example.dhall
    let replicate =
          https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/List/replicate sha256:b0e3ec1797b32c80c0bcb7e8254b08c7e9e35e75e6b410c7ac21477ab90167ad

in  replicate 5

... or, like dhall-format, there could be an --inplace option, too:

$ dhall freeze --inplace ./example.dhall
help wanted

All 12 comments

Yes! This would be amazing!

Trying to move up from trivial PRs/issues to more interesting ones. I like this one but I would need some advise on how to approach this.

Would someone be willing to give me some guidance?

@gilligan Sure! I think the rough idea is to parse Dhall files into expressions, but not resolve imports. Then, you'll need to write a function that walks the resulting Expr and looks for import statements. Whenever you see an import that doesn't have an integrity hash, you would need to fetch that file, normalise it, and calculate the integrity hash. Then, you would replace the users original import with an import that uses the integrity hash.

What that hash is, I'm not sure of, but it sounds like @Gabriel439 is suggesting sha256, whereas I was suggesting a Dhall expression hash (I think such a thing exists!?)

Basically, the key function you need to write should have this type:

freeze :: Import -> IO Import

... where it takes an Import, resolves it (using Dhall.Import.load), hashes it (using Dhall.Import.hashExpression) and sets the hash field of that import to the computed hash

Once you have that function then you can freeze the entire syntax tree using:

traverse freeze :: Expr s Import -> IO (Expr s Import)

Thanks, very helpful!

Since i'm bouncing between "ah, this is trivial" and "Huh, and now what?" i'll have to ask for some more help I'm afraid :)

I guess my main problem is that I don't understand the Expr data type and I thought that i could just be naive about it and navigate around it but it doesn't seem to be working out.

So what I am looking at right now is:

  • Some dhall input (Text)
  • Parsing dhall (exprAndHeaderFromText :: String -> Text -> Either ParseError (Expr Src Import)
  • Loading dhall (load :: Expr Src Import -> IO (Expr Src X))
  • Hashing (hashExpression :: Expr s X -> (Crypto.Hash.Digest SHA256))

    So I'm struggling with how to extract/operate on the Imports which I would need to write the freeze you outlined above out of Expr Src Import.

Sorry for the complications and thanks a lot for helping me understand all of this better.

Oh, one mistake: you would need to not use load (since if you resolve imports then there is nothing to freeze). You only need to parse the expression before freezing it

@Gabriel439 @ocharles still banging my head against the Dhall wall a bit here ;-}

The hashing with hashExpression works on Expr Src Import while my freeze function that i am traversing over an Expression over, deals with single Imports.

Do I have to load the import first after all or what's the deal with that or do I have to wrap the import in an Expr or how should I approach this? I'm not getting it right now..

Thanks a lot for the help.

@gilligan: Yes, you will need to load the import in order to be able to hash it with hashExpression. The trick is to wrap the Import in the Embed constructor to get an expression of type Expr Src Import which you can pass to Dhall.Import.load. I think it would be something like this:

freeze :: Import -> IO Import
freeze oldImport = do
    expression <- Dhall.Import.load (Embed import_)
    let newHash = Just (Dhall.Import.hashExpression expression)
    let oldImportHashed = importHashed oldImport
    let newImportHashed = oldImportHashed { hash = newHash }
    let newImport = oldImport { importHashed = newImportHashed }
    return newImport

@Gabriel439 Thanks a lot for your patience & support ;)

@gilligan: You're welcome! 馃檪

Fixed in #486 馃帀
Thanks @gilligan!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DrSensor picture DrSensor  路  5Comments

SiriusStarr picture SiriusStarr  路  5Comments

chris-martin picture chris-martin  路  5Comments

DrSensor picture DrSensor  路  6Comments

DrSensor picture DrSensor  路  4Comments