|
| 1 | +--- |
| 2 | +layout: multipage-overview |
| 3 | +title: Inkuire (Hoogle-like searches) |
| 4 | +partof: scala3-scaladoc |
| 5 | +num: 7 |
| 6 | +previous-page: site-versioning |
| 7 | +next-page: settings |
| 8 | +--- |
| 9 | + |
| 10 | +Searching for functions by their symbolic names can be time-consuming. |
| 11 | +That is why the new scaladoc allows to search for functions and fields by their types. |
| 12 | + |
| 13 | + |
| 14 | +So, for a declatation: |
| 15 | +``` |
| 16 | +extension [T](arr: IArray[T]) def span(p: T => Boolean): (IArray[T], IArray[T]) = ... |
| 17 | +``` |
| 18 | +Instead of searching for `span` we can also search for `IArray[A] => (A => Boolean) => (IArray[A], IArray[A])`. |
| 19 | + |
| 20 | +To use this feature simply type the signature of the function you are looking for in the scaladoc searchbar. This is how it works: |
| 21 | + |
| 22 | + |
| 23 | + |
| 24 | +This feature is provided by the [Inkuire](https://github.com/VirtusLab/Inkuire) search engine, which works for Scala 3 and Kotlin. To be up-to-date with the development of this feature, follow the [Inkuire repository](https://github.com/VirtusLab/Inkuire). |
| 25 | + |
| 26 | +## Example queries |
| 27 | + |
| 28 | +Some examples of queries with intended results: |
| 29 | +- `List[Int] => (Int => Long) => List[Long]` -> `map` |
| 30 | +- `Seq[A] => (A => B) => Seq[B]` -> `map` |
| 31 | +- `(A, B) => A` -> `_1` |
| 32 | +- `Set[Long] => Long => Boolean` -> `contains` |
| 33 | +- `Int => Long => Int` -> `const` |
| 34 | +- `String => Int => Char` -> `apply` |
| 35 | +- `(Int & Float) => (String | Double)` -> `toDouble`, `toString` |
| 36 | +- `F[A] => Int` -> `length` |
| 37 | + |
| 38 | +## Query syntax |
| 39 | + |
| 40 | +In order for a scaladoc searchbar query to be searched using Inkuire instead of the default search engine, the query has to contain the `=>` character sequence. |
| 41 | + |
| 42 | +Accepted input is similar to a curried function signature in Scala 3. With some differences: |
| 43 | +- AndTypes, OrTypes and Functions have to be enclosed in parentheses e.g. `(Int & Any) => String` |
| 44 | +- static fields and static functions that don't take parameters can be found by their type preceded with `=>` e.g. `=> Int` |
| 45 | +- A wildcard `_` can be used to indicate that we want to match any type in a given place e.g. `Long => Double => _` |
| 46 | +- Types in the form of single letter e.g. `A` or a letter with a digit `X1` are automatically assumed to be type variables |
| 47 | +- Other type variables can be declared just like in polymorphic functions e.g. `[AVariable, AlsoAVariable] => AVariable => AlsoAVariable => AVariable` |
| 48 | + |
| 49 | +## How it works |
| 50 | + |
| 51 | +Inkuire works as a JavaScript worker in the browser thanks to the power of [ScalaJS](https://www.scala-js.org/). |
| 52 | + |
| 53 | +To enable Inkuire when running scaladoc, add the flag `-Ygenerate-inkuire`. By adding this flag two files are generated: |
| 54 | +- `inkuire-db.json` - this is the file containing all the searchable declarations from the currently documented project in a format readable to the Inkuire search engine. |
| 55 | +- `inkuire-config.json` - this file contains the locations of the database files that should be searchable from the documentation of the current project. By default, it will be generated with the location of the local db file as well as the default implied locations of database files in [External mappings](/scala3/guides/scaladoc/settings.html#-external-mappings). |
| 56 | + |
| 57 | +## What to expect? |
| 58 | + |
| 59 | +When it comes to how the code is mapped to InkuireDb entries, there are some transformations to make the engine more opinionated (though open to suggestions and changes). Firstly, the receiver (non-module owner) of a function can be treated as a first argument. Automatic currying is also applied, so that the results don't depend on argument lists. When finding matches, `val`s and `def`s are not distinguished. |
| 60 | + |
| 61 | +So the following declarations should be found by query `Num => Int => Int => Int`: |
| 62 | +``` |
| 63 | +class Num(): |
| 64 | + def a(i: Int, j: Int): Int |
| 65 | + def b(i: Int)(j: Int): Int |
| 66 | + def c(i: Int): (Int => Int) |
| 67 | + val d: Int => Int => Int |
| 68 | + val e: Int => Int => Int |
| 69 | + val f: (Int, Int) => Int |
| 70 | +end Num |
| 71 | +
|
| 72 | +def g(i: Num, j: Int, k: Int): Int |
| 73 | +extension (i: Num) def h(j: Int, k: Int): Int |
| 74 | +def i(i: Num, j: Int)(k: Int): Int |
| 75 | +extension (i: Num) def j(j: Int)(k: Int): Int |
| 76 | +... |
| 77 | +``` |
| 78 | + |
| 79 | +When it comes to type aliases, they are desugared on both the declaration and the query signature. This means that for declarations: |
| 80 | +``` |
| 81 | +type Name = String |
| 82 | +
|
| 83 | +def fromName(name: Name): String |
| 84 | +def fromString(str: String): Name |
| 85 | +``` |
| 86 | +both methods, `fromName` and `fromString`, should be found for queries `Name => Name`, `String => String`, `Name => String` and `String => Name`. |
0 commit comments