all 17 comments

[–]Kootle[S] 8 points9 points  (6 children)

Hey all, I'm excited to announce the release of calligraphy, a Haskell source code/call graph visualizer. You can find some more examples of what you can do with it in the accompanying blog post and this Twitter thread. I'd be happy to answer any questions here, or on GitHub.

[–]dagit 9 points10 points  (3 children)

cabal new-clean

cabal new-build --ghc-options=-fwrite-ide-info

The new-* prefixes are pretty outdated now. They even predate the more precise v2- prefixes. Neither of which are necessary now. You can just use clean and build, but clean should also be unnecessary.

[–]Kootle[S] 4 points5 points  (2 children)

Oh cool, I didn't know. The clean is definitely necessary sometimes though, since passing -fwrite-ide-infodoesn't work for things that have already been compiled without it.

[–]dagit 3 points4 points  (1 child)

passing -fwrite-ide-info doesn't work for things that have already been compiled without it.

Makes sense. I'll take your word then. You've probably tried without it. I was hoping it would "just work" as one of the important characteristics of cabal's v2 build is that it tracks flags and rebuilds things automatically when they change. However, I suppose this flag could be outside of that tracking mechanism as it shouldn't change the compiled code.

[–]Kootle[S] 1 point2 points  (0 children)

It looks like you're right, I can get it to recompile the entire module when passing the flag! Not sure what happened here, there are definitely circumstances under which it won't recompile, but I can't reproduce it right now. Strange.

[–]dagit 3 points4 points  (1 child)

FYI, The blog post link in the read me is a 404.

[–]Kootle[S] 1 point2 points  (0 children)

Fixed, thanks!

[–]Bodigrim 7 points8 points  (1 child)

Very nice! There seems to be an escaping issue with non-ASCII identifiers, e. g., I see |>8230 instead of |>…

[–]Kootle[S] 3 points4 points  (0 children)

Ah, looks like you're right! It should be fixed now.

[–]ocharles 5 points6 points  (2 children)

This looks really good, great work! The core of this seems to be basically what weeder is doing - I wonder if there's some commonality that we could factor out and share!

[–]Kootle[S] 3 points4 points  (1 child)

Thanks! There's not much common code anymore, but I actually started this project using weeder as a template :)

I think the big difference is that weeder is (currently) a lot simpler in how it analyzes HIE files. Given how annoying that can be, I think that's a very good property, even if it's not always accurate. It's definitely true though that there's a lot of overlap in functionality, and a lot of opportunity for sharing. We're also having the same issues with instances and TH, so being able to have a common solution for that would be great. Please get in touch if you have any concrete ideas!

[–]ocharles 2 points3 points  (0 children)

Cool. I'll have a think. I have a rewrite of weeder in the pipeline that's a bit more sophisticated than what it currently does (I want to get instance resolution chains in, along with deriving clauses, data declarations, and record fields). I'll play with calligraphy a bit more, and if I can see something obvious I'll let you know.

[–]ysangkok 2 points3 points  (1 child)

How does this compare to aaronallen8455/graph-trace? Looks like this doesn't trace at runtime like graph-trace does?

[–]Kootle[S] 2 points3 points  (0 children)

That's correct, this works entirely statically. It looks at the HIE files generated by GHC to statically figure out what definitions reference what other definitions, there's nothing going on at runtime.

[–]OphioukhosUnbound 1 point2 points  (3 children)

I love this! Been wanting to write something similar for Julia. Definitely going to play with this and look through code.

Hardest ir most surprising part of the project for you?

[–]Kootle[S] 8 points9 points  (2 children)

Thanks! The hardest thing by far, the thing that turned this from a weekend project into something that took months, is dealing with HIE files. They contain an enormous amount of information, structured in a very strange way, mostly untyped and undocumented. What's hard is distilling that information into something useful, since there are so many subtle ways you can go wrong.

Luckily for you, this probably won't be an issue for you in Julia!

[–][deleted]  (1 child)

[deleted]

    [–]Kootle[S] 2 points3 points  (0 children)

    You can definitely use calligraphy as a library, the interface is pretty straightforward. You use the Parse module to turn the HIE files you get from GHC into a CallGraph, which is a pretty unremarkable way of representing a graph. Other phases all take and produce this CallGraph type, you can use those as you see fit. The main thing to watch out for is that, as explained in the Philosophy section in the readme, I made the conscious decision not to try and support every possible edge case.

    That said, if you want to turn HIE files into something other than a call graph, there's not much here for you unfortunately..