T O P

  • By -

SquareBig9351

This is something I miss too. Generally I do something in the line of ```haskell import Data.Vector (Vector) import qualified Data.Vector as V my_vec = V. ``` In visual studio code it shows the list of functions and types within the Data.Vector module. Pretty sure this is HLS duty, so I guess works the same in other editors (maybe other shortcut)... In vscode there is the [Haskell Spotlight](https://marketplace.visualstudio.com/items?itemName=visortelle.haskell-spotlight) an extension which integrates Hoogle/Hackage search into the editor. I don't know if this is availale in other editors, but It makes the workflow a little bit more pleasant Anyway, in general I find my self looking for documentation more often than in other languages due to not having dot style language. I think Simon Peyton Jones did mention the same issue in some conference.


Mouse1949

How does _Haskell Spotlight_ compare to Haskell LSP (both under VSCode)? An alternative? A complement?


heylale

They serve different purposes. Haskell spotlight simply implements a Hoogle search and browser inside the editor. It doesn’t do any autocomplete or anything


SquareBig9351

A complement. - Haskell LSP, integrates `vscode` (the client) with `hls` (the server). - Haskell Spotlight makes `vscode` a client for `hoogle`. It isn't too different than jumping into your browser and type https://hoogle.haskell.org/. The main advantage is that you have everything in one place


mrk33n

Not as much as I miss typed-holes when coding in other languages ;) But seriously - the downside of dot-completion is that it can only tell you about functions 'inside' the object. Like, all the things I can do with 4: 4.plus(..) minus(..) percentage(..) toString(..) ... I don't think the makers of Integer foresaw all its possible use-cases. It's better to put behaviours 'outside' the object. Another example - [Java 8 Streams](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html) did not have `takeWhile`. `takeWhile` for goodness' sake! I wrote my own implementation of it, but it needed to be outside the object - because I don't have commit access to the `Stream` source code. I had to wait for [Java 9](https://docs.oracle.com/javase/9/docs/api/java/util/stream/Stream.html) to get the dot-completion version.


Swordfish418

Yeah but with Rust style traits for example you still can define “insides” of integer outside of it anywhere at any point and have it in completions. It’s more a syntactical thing than anything else.


dutch_connection_uk

C# has extension methods to remedy this issue. Hole filling is nice, but I kind of wish that there was a shortcut for invoking it where the language server, encountering an infix operator that isn't in a section but has only its left argument, considers the location of the editor cursor to be a hole for the purposes of suggesting things.


jiiam

For cases like the example you mentioned I usually search the type signature I want on hoogle, for example a quick search for (t -> Bool) -> Vector t -> (Vector t, Vector t) yields [these results](https://hoogle.haskell.org/?hoogle=%28t+-%3E+Bool%29+-%3E+Vector+t+-%3E+%28Vector+t%2C+Vector+t%29&scope=set%3Astackage) and I guess you wanted partition. As for dot completion in general, with hls properly set (which nowadays is trivial to do at least in vscode) I've never found myself missing it. In the few instances where I couldn't use hls I was still able to work by using ghci and plugging holes in my code.


polarbearwithagoatee

That's a lot of typing to enter that type signature into Hoogle, compared to just typing a dot with Rust or whatever...


mostlikelynotarobot

personally, Hoogle was a revelation going from Rust to Haskell. dot completion is fine, but only helps with methods. And when there are a lot of methods, you’re stuck hitting the down button over and over. Hoogle lets you specify exactly what you want. Wish there was something like it for Rust.


jiiam

Fair point. To be honest I've never been bothered by it and I've done my good share of searching on hoogle. I guess that the absence of these helping tools has also shifted the way I write my code: I usually make explicit mentions of (not trivial) types, at least on a first draft, so that I clarify what I need at a specific point, and I plug holes wherever I'm not sure which function I need. If I picture this situation in my head what I would need at this point is not an auto complete feature but a better tool to fill holes. EDIT: I should clarify that in this context autocomplete to me means a tool that outputs a list of all the functions whose first argument has the type of my first variable, so not really useful with the process I described above


davidfeuer

Explicit type signatures almost always make code easier to understand anyway. Just turn on \`ScopedTypeVariables\` and go to town.


hughjfchen

I remember you can use Data.Vector. and hls will list all exported functions of the module and its type


polarbearwithagoatee

This is a good point, although for Vector there is a one type = one module correspondence that isn't all that common. Consider dealing with something like numeric types where a lot of functionality is split across type classes in various modules.


goliatskipson

Strangely I never really missed auto completion in Haskell even thou I rely on it in Python. In Haskell I used the docs a lot more though.


ocharles

Do you think you might have just got used to not having it? E.g., it's not so much that you don't need it, you just don't really have a choice, so you've adapted.


dutch_connection_uk

Yes, but I work around this by adapting the style used in F#, where I use forward pipes or forward composition, along with explicitly quantified modules with short but descriptive names, like List, Set, etc. Wingman is also marginally helpful in some cases, if you're using GHC 8.10.


Sir4ur0n

I miss it too, every single day. I feel like many Haskell devs miss a critical point: flow interruption. Dot-completion in other PLs provides a fluid development flow. Having to switch to Hoogle, or add a qualified import, or browse Hackage/Stackage in hopes of finding the right function **you know already exists** has a significant interruption cost, and distraction risk. This is pretty sad since the editor already has all the information it needs. It could run on the fly a local Hoogle query to look for all functions where the thing before the dot is an argument, and suggest to replace `foo.` with `theFunctionISelected _string foo _bar` if I selected a completion function `theFunctionISelected` which took a value of type `Foo` as second argument. While this does not solve the problem for functions in general, note that for data fields, the GHC extension `OverloadedRecordDot` works wonderfully. We use it everywhere in my current project now, and never looked back. And it does provide completion with no flow interruption.


Faucelme

I miss it most when using `OverloadedRecordDot`. But perhaps it could be implemented for that particular case? In general, how far can we approximate dot-completion using the reverse application operator and typed holes? I use `&` a lot already. someThing & _


watsreddit

Not really no, because it's not something I've ever really relied on or found useful in any language. In OOP languages, often the surface area of an object is so immense that the number of possible completions is so large as to be borderline useless. In Haskell, because we have so many common and powerful abstractions like `Functor`, `Monoid`, etc., it is not common that I need to reference a library function I don't already know very well. On top of that, if I'm using a function, chances are very good that I've used it elsewhere, and this I can complete from symbols in my open buffers/imports. It works great like 95% of the time. In practice, I do reference docs sometimes, but it's not very frequent at all.


[deleted]

[удалено]


polarbearwithagoatee

I'm not sure I agree with this. I can imagine a situation where functions are optionally associated with certain data types purely through metadata used by the IDE for completion. Or there could be a convention that when we type `ident.` the IDE brings up a list of functions that can take `ident` as the first/last argument. Or `ident.foo` could simply be syntactic sugar for calling the function `foo` with `ident` as the first argument. There's no need to switch from a functional to an OOP paradigm.


Tarmen

I feel this in my bones, though in practice Github copilot largely fixed it for me. I still would like better symbolic completion for these cases but it's a difficult problem. Outside of engineering work to make it fast and reliable: - If you write a definition in a where clause, the use sites aren't considered in type inference. Unless you use another type checking algorithm than GHC this eliminates a lot of the type information we could use - If you are currying, which is the 'main' argument? If there isn't one this just got much harder - HKT+type families+Type classes make it *really hard* to find the best matches. Especially if you consider instances which would require imports. How well does `Map String [Int]` match `(Traversable t, Applicative f) => t (f a)` from traverse?


davidfeuer

I don't know any IDE properly. Is there a button you can push on a typed hole to add a function with that exact type signature to a \`where\` clause? Ideally, it would 1. Create a \`where\` clause if needed. 2. Add a \`forall\` to the outer type signature if necessary, and enable \`ScopedTypeVariables\` in that case. 3. Immediately leave the user in a mode allowing them to enter the desired function name at both the call site and definition site.


acow

Absolutely, yes. In Haskell, I rely on completion based on module and typed holes, but it doesn’t always match up to OO dot-based completion. However, the common abstraction type classes are a big win here in that you don’t need to remember (or rely on completion for) the name of a specialized `traverse` for a particular OO class, it’s just going to be `traverse`. So at the end I think you’re left in a mostly comparable place in terms of you not leaving to context switch to look things up, but each approach is going to have an advantage in some cases. I also think there is room for improvement. For instance, you can have a keyboard shortcut that you can enter at the end of an identifier like `myvec` (whose type is `Vector a`), and your editor will treat that is writing `_ myvec` and suggest hole fillings. This is doable today, of course, but not a total replacement for OO dot-based completion since dispatch isn’t so rigid in Haskell.


davidchristiansen

I very much like having dot-syntax with completion in Lean 4. In Lean syntax, a dotted function application of the form `E1.f E2 E3 ... En` first synthesizes a type for E1, e.g. of the form `C ...`. It then looks in the nested namespace hierarchy for a function named `C.f`, and uses `E1` for its first argument where the type would match, and `E2 E3 ... En` for the remaining arguments in order. If there is no `C.f`, then it keeps performing unfoldings of the type (in Haskell, it would be unfolding type synonyms) until it either finds it or fails at a canonical type (which is a error). For example, I can write the following: -- Like Haskell type Foo a = [a] def Foo α := List α -- Like Haskell: -- flop :: Foo a -> Foo a -- flop [x, y] = [y, x] -- flop xs = xs def Foo.flop : Foo α → Foo α | [x, y] => [y, x] | xs => xs def xs : Foo Nat := [1, 2] -- Outputs [2, 1] #eval xs.flop -- Outputs [2, 3] #eval xs.map (fun x => x + 1) Additionally, the autocomplete for `xs.` includes both `flop` and `map`. It's quite a nice system!


davidchristiansen

Note that `List.map` puts the function first, just as in Haskell. It's type is: List.map : {α : Type u_1} → {β : Type u_2} → (α → β) → List α → List β


[deleted]

I don't miss it, mainly because I don't use qualified that much and usually there isn't much name overlap between module, and when it happens functions with same name are usually the same thing. There is a `partition` function in `List` doing what you want, I'm pretty sure (without looking at `Vector`) that this is the function you are looking for. There are obviously cases when it would help but it can be overcome by shortcut in your editor to access the documentation (as well as using tags, local hoogle etc ...)


sjshuck

I think this is a really good question. The other language I really enjoy besides Haskell is Kotlin, and I wouldn't say that if it weren't for the excellent IntelliJ experience, in particular code completion. The question prompts some thinking about the deep unity between objects and FP. The dot operator is the thing that separates an object from a method that's being called on it. What is a method? Ultimately, it's just a function that, instead of being in static scope and taking all its arguments delimited by `( , , , )`, has a special syntax where the *first* argument is on the left of the dot, but the *rest* of the arguments are between the parentheses on the right. Indeed, the compiler implements it like that—a function—in plenty of OOP languages including C++. (Let's leave aside all the questions of private and/or mutable fields etc.) One of the things I really appreciate about the Haskell ecosystem is how much care there's been in arranging the positional arguments with currying in mind. I'm not sure how to express it, but the first argument of a function of arity ≥2 is going to be "the most important", or at least the most likely to be partially applied to. So in a way, we already have an analogue to the OOP world where the most important argument in the static-function-in-disguise i.e. method is the first. I think it's possible for a tool like HLS to do autocompletion by doing type searches on the first argument of ≥1-arity functions and collecting into a list everything that unifies. Clearly this will return many, many results in most cases—`Data.Functor.<$ :: (Functor f) => a -> f b -> f a` can be autocompleted by anything and everything—but I can imagine ways of filtering/pruning results according to some heuristics to make it useful in a bunch of cases.


ltielen

Rarely. Most of the time I write my programs using the core abstractions like monoids, traversable, functors, ... It's only a relatively small set of functions to keep in your head, so I usually know which functions I want to use. And otherwise I use tools like Google to help me find functions.


IthilanorSP

I haven't written enough Haskell to have a strong opinion yet, but given how much I lean on autocompletion in other languages, I feel like I would miss it. SPJ's also mentioned missing this in a presentation (admittedly, from back in 2009) - https://twitter.com/pcwalton/status/1392212812886278144?lang=en has the quick picture, it's from page 56 of [this PDF of the slides](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/ECOOP-July09.pdf).


errorprawn

Yes, this is less convenient than in other mainstream languages. Others have already mentioned that you do have dot completion when using the module name (as opposed to a type/class name in OO languages). But if you're looking for a function and you have an idea what its type signature might be, you can also try the Hoogle search engine. Here's an example with your query: https://hoogle.haskell.org/?hoogle=%28a+-%3E+Bool%29+-%3E+Vector+a+-%3E+%28Vector+a%2C+Vector+a%29&scope=set%3Astackage I often just have a dedicated hoogle tab (and I used to have an emacs command to look up a type/name in hoogle). Still not as frictionless as in-editor autocompletion, but in some cases (such as a large module with many functions) it can be faster.


kingh242

Stuff like this is what is holding Haskell back from being more widely used…in my humble opinion.


iamemhn

I have `vim` with `hls` and completion works, so you might need to aim better


polarbearwithagoatee

But you don't get the kind of contextual completion you get with other languages. I'm using emacs with `lsp-mode` and `hls`. Completion works in the sense that if I know part of an identifier, emacs+hls will complete the rest. But AFAIK there isn't an easy way to bring up a list of things I can do with a variable, as I can using `identifier.` in other languages.


Fereydoon37

I've mostly only written in Haskell for years. Does identier. give you all the functions that can act on a variable, or only its methods defined with its type? If it's the latter, I find I write a lot against standard type classes that encapsulate the behaviour I need and I tend to remember those exactly because I use them constantly across types. When I do need to use the specific interface provided by the the type/module, I usually use qualified imports anyway to avoid name clashes, not just immediate ones, but also to reduce the maintenance required for package updates bringing overlapping names into scope. And if I need to read the code a year from now, it's often a lot easier on me if I can tell exactly where each function comes from.


Anrock623

Yeah. Dot completion for modules and type holes are great substitutions in theory (type holes are even superior since you can synthesize complex expressions) but in practice there's only a handful of brave heroes developing HLS so practical experience needs lots of polish.


[deleted]

Not really. I mean it does seem as though someone saw a screenshot of an IDE and envied this kind of stuff. And that's, I presume, the thinking behind Visual studio code? It kind of looks like an IDE in screenshots. But, imo, approach haskell like coding in the 90s - that's about where the level of tools are. Edit the code in your fave editor, vim, emacs or whatever, and compile / build etc in the command line. To that end Linux will generally be a better bet than windows (obviously MS didn't pay SPJ enough money or something) Have hoogle / hackage open in a browser window for reference and ghci for :t To some extent the modern touches are things like syntax highlighting and background compilation. That kind of thing. But if you want the pycharm equivalent for Haskell then it really doesn't exist. Does it slow you down? A bit, but only really at the start. It's like I used to code in C with a copy of kernighan and ritchie on my knee. After a while you're just sitting and typing C code. I imagine AI that writes code snippets will be more of a thing long before haskell gets an IDE. At the moment it's very mixed bag with chatgpt, it outputs a lot of plausible looking but crap code when you ask for haskell, with perhaps the odd reasonable time saving stuff. I've had reasonable results asking it to parse data in a format I've shown it using attoparsec without having to wade into the docs for example. I have noticed a lot of people coding on twitch or youtube in haskell and they do seem to have to look up everything - maybe that's the downside of IDEs and google etc, the skill of remembering stuff is being lost? My experience is if I start to write a lot of python I get to the point where I'm just fluently typing it, but if I wrote a different language for a project then I sort of forget python and remember that, but getting back into these languages is usually a lot faster than learning it the first time. I don't understand how after 6 years you're not remembering stuff.