Searching for functions

Thanks for this useful question! I am curious (if you feel comfortable sharing) your interest in approaching OCaml via Haskell. What do you feel are some of OCaml’s advantages for your use case?

Thanks for this! I’d like to see odig support some sort of search functionality, that would really help someone like myself get orientated quicker.

At the moment I don’t have a use case in mind - I’ve just heard good things about ocaml and I’m curious to explore more. The language certainly has some cool features I’d like to have in Haskell (module functors, polymorphic variants, labelled arguments…etc), and without the zoo of extensions and exotic type-level things it does feel a bit more “industrial”. But I’m not sure I can give up purity having seen how useful it is in Haskell…

I have been particularly impressed by the Bucklescript project - specifically the quality of the generated JS code - and could see myself using that on the frontend for non-trivial projects.

2 Likes

I’d like to add that at some point aswell, but don’t hold your breath for now.

A low effort improvement with great usability returns would be for odoc to at least generate per package index pages (like ocamldoc did), this issue tracks this.

1 Like

It would definitely be nice to have something like hoogle, with global function name and type signature search. Elm has something like that as well.

1 Like

Merlin has a search feature, :MerlinSearch in vim or M-x merlin-search in emacs.
The command takes a query which is a list of type names prefixed by - or +:

  • - for the type of values you have and the function you are looking for can consume
  • + for values you would like to get.

For instance, :MerlinSearch -int +string gives you ways to transform an integer to a string:

string_of_int                             V int -> string                   
Bytes.create                              V int -> bytes                    
BytesLabels.create                        V int -> bytes                    
String.create                             V int -> bytes                    
StringLabels.create                       V int -> bytes                    
Big_int.approx_big_int                    V int -> Big_int.big_int -> string
Num.approx_num_exp                        V int -> Num.num -> string        
Num.approx_num_fix                        V int -> Num.num -> string        
Ratio.approx_ratio_exp                    V int -> Ratio.ratio -> string    
Ratio.approx_ratio_fix                    V int -> Ratio.ratio -> string    
...

Parametric types are flattened (only variance is considered), so String.concat can be found with -string -list +string. I haven’t thought about filtering based on the name of values, but this should be easy to implement in a future version of merlin, e.g. env -string +string for finding string transformers that contains “env” in their name.

The UI has not seen much work and the search is blocking, which is not super convenient, help is welcome to improve that :).

18 Likes

That’s really cool. I’d like to play around with it on the command line, are there any docs? I looked at https://github.com/ocaml/merlin/blob/master/doc/dev/PROTOCOL.md and ran ocamlmerlin single -commands-help, but not seeing anything about a search functionality.

Never mind, I figured it out :slight_smile:

echo '\n' | ocamlmerlin single search-by-polarity -position 0 -query "-int +string" | less

Output is JSON, so would look better with some formatting:

echo '\n' | ocamlmerlin single search-by-polarity -position 0 -query "-int +string" | jq . | less
2 Likes

Here are two small bash/zsh functions building on @yawaramin’s work that might be useful. They will display a list of functions + the signatures. ocs will only display the first 10 because I don’t always want to fill my screen where as osca will list them all.

ocs () { echo '\n' | ocamlmerlin single search-by-polarity -position 0 -query $1 | jq '.value.entries[] | "\(.name) = \(.desc)"' | head -n 10 }
ocsa () { echo '\n' | ocamlmerlin single search-by-polarity -position 0 -query $1 | jq '.value.entries[] | "\(.name) = \(.desc)"' }
2 Likes

Fantastic! Minor quibble, but I’d use : instead of = to separate the name and type. Also another option for the output is to pipe to less.

1 Like

Does it search through all the opam-installed libraries?
Or just the currently opened project?
Why not support plain type signatures like int -> string, why do you need the - and +?
I will give it a try for sure.

Searching by type signatures would be super useful too.

Honestly, the OCaml community doesn’t have something that come close to Haskell’s hoogle.
Which is really annoying when you know the productivity boost hoogle allows…

Does it search through all the opam-installed libraries?
Or just the currently opened project?

It searches only in the project scope. This is a proof-of-concept, but it can easily be enhanced to do opam-wide search (I didn’t think about it because that I never felt the need).
Also, the search space can easily be indexed (while right now it is doing linear search), so performance should not be a problem at scale. I need to talk to @dbuenzli about that :).

Why not support plain type signatures like int → string, why do you need the - and +?

With the current spec, the search is directed by variance which more accurately reflects the flow of information than a mere ->. If it is just about syntax, of course the query language can be reworked, it is just frontend work (similarly, Hoogle does not strictly search by signature and do some approximation).

By relying only on variance, values are found even if they execute in a different monad, are written in a continuation passing-style or need more contexts (e.g. a context value).
Results can then be ranked by relevance (though that bit has subjective elements in it).

4 Likes

Even if the project looks asleep, it may be interesting to mention it here : https://github.com/camlspotter/ocamloscope.2.

That’s really neat, but it’s not clear to me how to search for generic types.

For example, M-x merlin-search RET -'a list +'a gives me an error:

"exception":"Not_found
Raised at file \"src/ocaml/typing/405/ident.ml\", line 168, characters 6-21
Called from file \"src/ocaml/typing/405/env.ml\", line 183, characters 22-43
Called from file \"src/ocaml/typing/405/env.ml\" (inlined), line 1057, characters 2-57
Called from file \"src/ocaml/typing/405/env.ml\", line 1127, characters 26-50
Called from file \"src/analysis/polarity_search.ml\", line 83, characters 29-52
Called from file \"src/utils/std.ml\", line 100, characters 12-15
Called from file \"src/utils/std.ml\", line 102, characters 23-39
Called from file \"src/analysis/polarity_search.ml\", line 86, characters 17-62
Called from file \"src/frontend/query_commands.ml\", line 403, characters 6-125
Called from file \"src/utils/local_store.ml\", line 29, characters 8-12
Re-raised at file \"src/utils/local_store.ml\", line 37, characters 4-15
Called from file \"src/kernel/mocaml.ml\", line 40, characters 8-38
Re-raised at file \"src/kernel/mocaml.ml\", line 48, characters 4-15
Called from file \"src/frontend/new/new_commands.ml\", line 64, characters 15-53
Called from file \"src/utils/std.ml\", line 654, characters 8-12
Re-raised at file \"src/utils/std.ml\", line 656, characters 30-39
Called from file \"src/ocaml/utils/misc.ml\", line 30, characters 20-27
Re-raised at file \"src/ocaml/utils/misc.ml\", line 30, characters 50-57
Called from file \"src/frontend/new/new_merlin.ml\", line 97, characters 18-54
"

Does the search syntax support this? I had a look at the merlin README and wiki, but didn’t see anything about this feature.

No, type parameters are ignored. Just write -list.

Aha, so it’s only concrete types? Could I find List.for_all and List.exists using this? In this case I have a function 'a -> bool and a 'a list and I was wondering if merlin search could handle this.

-list -bool gives me List.find as first result.
But adding variable to the query language would be valuable.

hi,

maybe sourcegraph (https://sourcegraph.com/welcome#code-navigation) could help on the case of Base, since it’s at github. Here just used it (https://sourcegraph.com/github.com/janestreet/base@master/-/blob/src/sys0.ml#L35:5-35:11) via the sourcegraph-firefox plugin to look for the ‘symbol’ ‘getenv’, it was helpful :grimacing:

2 Likes