1-- | This module provides documentation for the builtin Prim modules.
2module Language.PureScript.Docs.Prim
3  ( primDocsModule
4  , primRowDocsModule
5  , primTypeErrorDocsModule
6  , primModules
7  ) where
8
9import Prelude.Compat hiding (fail)
10import Data.Functor (($>))
11import Data.Text (Text)
12import qualified Data.Text as T
13import qualified Data.Map as Map
14import Language.PureScript.Docs.Types
15
16import qualified Language.PureScript.Crash as P
17import qualified Language.PureScript.Environment as P
18import qualified Language.PureScript.Names as P
19
20primModules :: [Module]
21primModules =
22  [ primDocsModule
23  , primBooleanDocsModule
24  , primCoerceDocsModule
25  , primOrderingDocsModule
26  , primRowDocsModule
27  , primRowListDocsModule
28  , primSymbolDocsModule
29  , primTypeErrorDocsModule
30  ]
31
32primDocsModule :: Module
33primDocsModule = Module
34  { modName = P.moduleNameFromString "Prim"
35  , modComments = Just $ T.unlines
36      [ "The `Prim` module is embedded in the PureScript compiler in order to provide compiler support for certain types — for example, value literals, or syntax sugar. It is implicitly imported unqualified in every module except those that list it as a qualified import."
37      , ""
38      , "`Prim` does not include additional built-in types and kinds that are defined deeper in the compiler such as Type wildcards (e.g. `f :: _ -> Int`) and Quantified Types. Rather, these are documented in [the PureScript language reference](https://github.com/purescript/documentation/blob/master/language/Types.md)."
39      ]
40  , modDeclarations =
41      [ function
42      , array
43      , record
44      , number
45      , int
46      , string
47      , char
48      , boolean
49      , partial
50      , kindType
51      , kindConstraint
52      , kindSymbol
53      , kindRow
54      ]
55  , modReExports = []
56  }
57
58primBooleanDocsModule :: Module
59primBooleanDocsModule = Module
60  { modName = P.moduleNameFromString "Prim.Boolean"
61  , modComments = Just "The Prim.Boolean module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains a type level `Boolean` data structure."
62  , modDeclarations =
63      [ booleanTrue
64      , booleanFalse
65      ]
66  , modReExports = []
67  }
68
69primCoerceDocsModule :: Module
70primCoerceDocsModule = Module
71  { modName = P.moduleNameFromString "Prim.Coerce"
72  , modComments = Just "The Prim.Coerce module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains an automatically solved type class for coercing types that have provably-identical runtime representations with [purescript-safe-coerce](https://pursuit.purescript.org/packages/purescript-safe-coerce)."
73  , modDeclarations =
74      [ coercible
75      ]
76  , modReExports = []
77  }
78
79primOrderingDocsModule :: Module
80primOrderingDocsModule = Module
81  { modName = P.moduleNameFromString "Prim.Ordering"
82  , modComments = Just "The Prim.Ordering module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains a type level `Ordering` data structure."
83  , modDeclarations =
84      [ kindOrdering
85      , orderingLT
86      , orderingEQ
87      , orderingGT
88      ]
89  , modReExports = []
90  }
91
92primRowDocsModule :: Module
93primRowDocsModule = Module
94  { modName = P.moduleNameFromString "Prim.Row"
95  , modComments = Just "The Prim.Row module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains automatically solved type classes for working with row types."
96  , modDeclarations =
97      [ union
98      , nub
99      , lacks
100      , rowCons
101      ]
102  , modReExports = []
103  }
104
105primRowListDocsModule :: Module
106primRowListDocsModule = Module
107  { modName = P.moduleNameFromString "Prim.RowList"
108  , modComments = Just "The Prim.RowList module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains a type level list (`RowList`) that represents an ordered view of a row of types."
109  , modDeclarations =
110      [ kindRowList
111      , rowListCons
112      , rowListNil
113      , rowToList
114      ]
115  , modReExports = []
116  }
117
118primSymbolDocsModule :: Module
119primSymbolDocsModule = Module
120  { modName = P.moduleNameFromString "Prim.Symbol"
121  , modComments = Just "The Prim.Symbol module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains automatically solved type classes for working with `Symbols`."
122  , modDeclarations =
123      [ symbolAppend
124      , symbolCompare
125      , symbolCons
126      ]
127  , modReExports = []
128  }
129
130primTypeErrorDocsModule :: Module
131primTypeErrorDocsModule = Module
132  { modName = P.moduleNameFromString "Prim.TypeError"
133  , modComments = Just "The Prim.TypeError module is embedded in the PureScript compiler. Unlike `Prim`, it is not imported implicitly. It contains type classes that provide custom type error and warning functionality."
134  , modDeclarations =
135      [ warn
136      , fail
137      , kindDoc
138      , textDoc
139      , quoteDoc
140      , quoteLabelDoc
141      , besideDoc
142      , aboveDoc
143      ]
144  , modReExports = []
145  }
146
147type NameGen a = Text -> P.Qualified (P.ProperName a)
148
149unsafeLookupOf
150  :: forall v (a :: P.ProperNameType)
151  . NameGen a
152  -> Map.Map (P.Qualified (P.ProperName a)) v
153  -> String
154  -> Text
155  -> v
156unsafeLookupOf k m errorMsg name = go name
157  where
158  go = fromJust' . flip Map.lookup m . k
159
160  fromJust' (Just x) = x
161  fromJust' _ = P.internalError $ errorMsg ++ show name
162
163lookupPrimTypeKindOf
164  :: NameGen 'P.TypeName
165  -> Text
166  -> Type'
167lookupPrimTypeKindOf k = ($> ()) . fst . unsafeLookupOf k
168  ( P.primTypes <>
169    P.primBooleanTypes <>
170    P.primOrderingTypes <>
171    P.primRowTypes <>
172    P.primRowListTypes <>
173    P.primTypeErrorTypes
174  ) "Docs.Prim: No such Prim type: "
175
176primType :: Text -> Text -> Declaration
177primType = primTypeOf P.primName
178
179primTypeOf :: NameGen 'P.TypeName -> Text -> Text -> Declaration
180primTypeOf gen title comments = Declaration
181  { declTitle = title
182  , declComments = Just comments
183  , declSourceSpan = Nothing
184  , declChildren = []
185  , declInfo = ExternDataDeclaration (lookupPrimTypeKindOf gen title)
186  , declKind = Nothing
187  }
188
189-- | Lookup the TypeClassData of a Prim class. This function is specifically
190-- not exported because it is partial.
191lookupPrimClassOf :: NameGen 'P.ClassName -> Text -> P.TypeClassData
192lookupPrimClassOf g = unsafeLookupOf g
193  ( P.primClasses <>
194    P.primCoerceClasses <>
195    P.primRowClasses <>
196    P.primRowListClasses <>
197    P.primSymbolClasses <>
198    P.primTypeErrorClasses
199  ) "Docs.Prim: No such Prim class: "
200
201primClass :: Text -> Text -> Declaration
202primClass = primClassOf P.primName
203
204primClassOf :: NameGen 'P.ClassName -> Text -> Text -> Declaration
205primClassOf gen title comments = Declaration
206  { declTitle = title
207  , declComments = Just comments
208  , declSourceSpan = Nothing
209  , declChildren = []
210  , declInfo =
211      let
212        tcd = lookupPrimClassOf gen title
213        args = fmap (fmap ($> ())) <$> P.typeClassArguments tcd
214        superclasses = ($> ()) <$> P.typeClassSuperclasses tcd
215        fundeps = convertFundepsToStrings args (P.typeClassDependencies tcd)
216      in
217        TypeClassDeclaration args superclasses fundeps
218  , declKind = Nothing
219  }
220
221kindType :: Declaration
222kindType = primType "Type" $ T.unlines
223  [ "`Type` is the kind of all proper types: those that classify value-level terms."
224  , "For example the type `Boolean` has kind `Type`; denoted by `Boolean :: Type`."
225  ]
226
227kindConstraint :: Declaration
228kindConstraint = primType "Constraint" $ T.unlines
229  [ "`Constraint` is the kind of type class constraints."
230  , "For example, a type class declaration like this:"
231  , ""
232  , "    class Semigroup a where"
233  , "      append :: a -> a -> a"
234  , ""
235  , "has the kind signature:"
236  , ""
237  , "    class Semigroup :: Type -> Constraint"
238  ]
239
240kindSymbol :: Declaration
241kindSymbol = primType "Symbol" $ T.unlines
242  [ "`Symbol` is the kind of type-level strings."
243  , ""
244  , "Construct types of this kind using the same literal syntax as documented"
245  , "for strings."
246  ]
247
248kindRow :: Declaration
249kindRow = primType "Row" $ T.unlines
250  [ "`Row` is the kind constructor of label-indexed types which map type-level strings to other types."
251  , "For example, the kind of `Record` is `Row Type -> Type`, mapping field names to values."
252  ]
253
254function :: Declaration
255function = primType "Function" $ T.unlines
256  [ "A function, which takes values of the type specified by the first type"
257  , "parameter, and returns values of the type specified by the second."
258  , "In the JavaScript backend, this is a standard JavaScript Function."
259  , ""
260  , "The type constructor `(->)` is syntactic sugar for this type constructor."
261  , "It is recommended to use `(->)` rather than `Function`, where possible."
262  , ""
263  , "That is, prefer this:"
264  , ""
265  , "    f :: Number -> Number"
266  , ""
267  , "to either of these:"
268  , ""
269  , "    f :: Function Number Number"
270  , "    f :: (->) Number Number"
271  ]
272
273array :: Declaration
274array = primType "Array" $ T.unlines
275  [ "An Array: a data structure supporting efficient random access. In"
276  , "the JavaScript backend, values of this type are represented as JavaScript"
277  , "Arrays at runtime."
278  , ""
279  , "Construct values using literals:"
280  , ""
281  , "    x = [1,2,3,4,5] :: Array Int"
282  ]
283
284record :: Declaration
285record = primType "Record" $ T.unlines
286  [ "The type of records whose fields are known at compile time. In the"
287  , "JavaScript backend, values of this type are represented as JavaScript"
288  , "Objects at runtime."
289  , ""
290  , "The type signature here means that the `Record` type constructor takes"
291  , "a row of concrete types. For example:"
292  , ""
293  , "    type Person = Record (name :: String, age :: Number)"
294  , ""
295  , "The syntactic sugar with curly braces `{ }` is generally preferred, though:"
296  , ""
297  , "    type Person = { name :: String, age :: Number }"
298  , ""
299  , "The row associates a type to each label which appears in the record."
300  , ""
301  , "_Technical note_: PureScript allows duplicate labels in rows, and the"
302  , "meaning of `Record r` is based on the _first_ occurrence of each label in"
303  , "the row `r`."
304  ]
305
306number :: Declaration
307number = primType "Number" $ T.unlines
308  [ "A double precision floating point number (IEEE 754)."
309  , ""
310  , "Construct values of this type with literals:"
311  , ""
312  , "    y = 35.23 :: Number"
313  , "    z = 1.224e6 :: Number"
314  ]
315
316int :: Declaration
317int = primType "Int" $ T.unlines
318  [ "A 32-bit signed integer. See the purescript-integers package for details"
319  , "of how this is accomplished when compiling to JavaScript."
320  , ""
321  , "Construct values of this type with literals:"
322  , ""
323  , "    x = 23 :: Int"
324  ]
325
326string :: Declaration
327string = primType "String" $ T.unlines
328  [ "A String. As in JavaScript, String values represent sequences of UTF-16"
329  , "code units, which are not required to form a valid encoding of Unicode"
330  , "text (for example, lone surrogates are permitted)."
331  , ""
332  , "Construct values of this type with literals, using double quotes `\"`:"
333  , ""
334  , "    x = \"hello, world\" :: String"
335  , ""
336  , "Multi-line string literals are also supported with triple quotes (`\"\"\"`)."
337  ]
338
339char :: Declaration
340char = primType "Char" $ T.unlines
341   [ "A single character (UTF-16 code unit). The JavaScript representation is a"
342   , "normal String, which is guaranteed to contain one code unit. This means"
343   , "that astral plane characters (i.e. those with code point values greater"
344   , "than 0xFFFF) cannot be represented as Char values."
345   , ""
346   , "Construct values of this type with literals, using single quotes `'`:"
347   , ""
348   , "    x = 'a' :: Char"
349   ]
350
351boolean :: Declaration
352boolean = primType "Boolean" $ T.unlines
353  [ "A JavaScript Boolean value."
354  , ""
355  , "Construct values of this type with the literals `true` and `false`."
356  ]
357
358partial :: Declaration
359partial = primClass "Partial" $ T.unlines
360  [ "The Partial type class is used to indicate that a function is *partial,*"
361  , "that is, it is not defined for all inputs. In practice, attempting to use"
362  , "a partial function with a bad input will usually cause an error to be"
363  , "thrown, although it is not safe to assume that this will happen in all"
364  , "cases. For more information, see"
365  , "[purescript-partial](https://pursuit.purescript.org/packages/purescript-partial/)."
366  ]
367
368booleanTrue :: Declaration
369booleanTrue = primTypeOf (P.primSubName "Boolean") "True" $ T.unlines
370  [ "The 'True' boolean type."
371  ]
372
373booleanFalse :: Declaration
374booleanFalse = primTypeOf (P.primSubName "Boolean") "False" $ T.unlines
375  [ "The 'False' boolean type."
376  ]
377
378coercible :: Declaration
379coercible = primClassOf (P.primSubName "Coerce") "Coercible" $ T.unlines
380  [ "Coercible is a two-parameter type class that has instances for types `a`"
381  , "and `b` if the compiler can infer that they have the same representation."
382  , "Coercible constraints are solved according to the following rules:"
383  , ""
384  , "* _reflexivity_, any type has the same representation as itself:"
385  , "`Coercible a a` holds."
386  , ""
387  , "* _symmetry_, if a type `a` can be coerced to some other type `b`, then `b`"
388  , "can also be coerced back to `a`: `Coercible a b` implies `Coercible b a`."
389  , ""
390  , "* _transitivity_, if a type `a` can be coerced to some other type `b` which"
391  , "can be coerced to some other type `c`, then `a` can also be coerced to `c`:"
392  , "`Coercible a b` and `Coercible b c` imply `Coercible a c`."
393  , ""
394  , "* Newtypes can be freely wrapped and unwrapped when their constructor is"
395  , "in scope:"
396  , ""
397  , "      newtype Age = Age Int"
398  , ""
399  , "`Coercible Int Age` and `Coercible Age Int` hold since `Age` has the same"
400  , "runtime representation than `Int`."
401  , ""
402  , "Newtype constructors have to be in scope to preserve abstraction. It's"
403  , "common to declare a newtype to encode some invariants (non emptiness of"
404  , "arrays with `Data.Array.NonEmpty.NonEmptyArray` for example), hide its"
405  , "constructor and export smart constructors instead. Without this restriction,"
406  , "the guarantees provided by such newtypes would be void."
407  , ""
408  , "* If none of the above are applicable, two types of kind `Type` may be"
409  , "coercible, but only if their heads are the same. For example,"
410  , "`Coercible (Maybe a) (Either a b)` does not hold because `Maybe` and"
411  , "`Either` are different. Those types don't share a common runtime"
412  , "representation so coercing between them would be unsafe. In addition their"
413  , "arguments may need to be identical or coercible, depending on the _roles_"
414  , "of the head's type parameters. Roles are documented in [the PureScript"
415  , "language reference](https://github.com/purescript/documentation/blob/master/language/Roles.md)."
416  , ""
417  , "Coercible being polykinded, we can also coerce more than types of kind `Type`:"
418  , ""
419  , "* Rows are coercible when they have the same labels, when the corresponding"
420  , "pairs of types are coercible and when their tails are coercible:"
421  , "`Coercible ( label :: a | r ) ( label :: b | s )` holds when"
422  , "`Coercible a b` and `Coercible r s` do. Closed rows cannot be coerced to"
423  , "open rows."
424  , ""
425  , "* Higher kinded types are coercible if they are coercible when fully"
426  , "saturated: `Coercible (f :: _ -> Type) (g :: _ -> Type)` holds when"
427  , "`Coercible (f a) (g a)` does."
428  , ""
429  , "This rule may seem puzzling since there is no term of type `_ -> Type` to"
430  , "apply `coerce` to, but it is necessary when coercing types with higher"
431  , "kinded parameters."
432  ]
433
434kindOrdering :: Declaration
435kindOrdering = primTypeOf (P.primSubName "Ordering") "Ordering" $ T.unlines
436  [ "The `Ordering` kind represents the three possibilities of comparing two"
437  , "types of the same kind: `LT` (less than), `EQ` (equal to), and"
438  , "`GT` (greater than)."
439  ]
440
441orderingLT :: Declaration
442orderingLT = primTypeOf (P.primSubName "Ordering") "LT" $ T.unlines
443  [ "The 'less than' ordering type."
444  ]
445
446orderingEQ :: Declaration
447orderingEQ = primTypeOf (P.primSubName "Ordering") "EQ" $ T.unlines
448  [ "The 'equal to' ordering type."
449  ]
450
451orderingGT :: Declaration
452orderingGT = primTypeOf (P.primSubName "Ordering") "GT" $ T.unlines
453  [ "The 'greater than' ordering type."
454  ]
455
456union :: Declaration
457union = primClassOf (P.primSubName "Row") "Union" $ T.unlines
458  [ "The Union type class is used to compute the union of two rows of types"
459  , "(left-biased, including duplicates)."
460  , ""
461  , "The third type argument represents the union of the first two."
462  ]
463
464nub :: Declaration
465nub = primClassOf (P.primSubName "Row") "Nub" $ T.unlines
466  [ "The Nub type class is used to remove duplicate labels from rows."
467  ]
468
469lacks :: Declaration
470lacks = primClassOf (P.primSubName "Row") "Lacks" $ T.unlines
471  [ "The Lacks type class asserts that a label does not occur in a given row."
472  ]
473
474rowCons :: Declaration
475rowCons = primClassOf (P.primSubName "Row") "Cons" $ T.unlines
476  [ "The Cons type class is a 4-way relation which asserts that one row of"
477  , "types can be obtained from another by inserting a new label/type pair on"
478  , "the left."
479  ]
480
481kindRowList :: Declaration
482kindRowList = primTypeOf (P.primSubName "RowList") "RowList" $ T.unlines
483  [ "A type level list representation of a row of types."
484  ]
485
486rowListCons :: Declaration
487rowListCons = primTypeOf (P.primSubName "RowList") "Cons" $ T.unlines
488  [ "Constructs a new `RowList` from a label, a type, and an existing tail"
489  , "`RowList`.  E.g: `Cons \"x\" Int (Cons \"y\" Int Nil)`."
490  ]
491
492rowListNil :: Declaration
493rowListNil = primTypeOf (P.primSubName "RowList") "Nil" $ T.unlines
494  [ "The empty `RowList`."
495  ]
496
497rowToList :: Declaration
498rowToList = primClassOf (P.primSubName "RowList") "RowToList" $ T.unlines
499  [ "Compiler solved type class for generating a `RowList` from a closed row"
500  , "of types.  Entries are sorted by label and duplicates are preserved in"
501  , "the order they appeared in the row."
502  ]
503
504symbolAppend :: Declaration
505symbolAppend = primClassOf (P.primSubName "Symbol") "Append" $ T.unlines
506  [ "Compiler solved type class for appending `Symbol`s together."
507  ]
508
509symbolCompare :: Declaration
510symbolCompare = primClassOf (P.primSubName "Symbol") "Compare" $ T.unlines
511  [ "Compiler solved type class for comparing two `Symbol`s."
512  , "Produces an `Ordering`."
513  ]
514
515symbolCons :: Declaration
516symbolCons = primClassOf (P.primSubName "Symbol") "Cons" $ T.unlines
517  [ "Compiler solved type class for either splitting up a symbol into its"
518  , "head and tail or for combining a head and tail into a new symbol."
519  , "Requires the head to be a single character and the combined string"
520  , "cannot be empty."
521  ]
522
523fail :: Declaration
524fail = primClassOf (P.primSubName "TypeError") "Fail" $ T.unlines
525  [ "The Fail type class is part of the custom type errors feature. To provide"
526  , "a custom type error when someone tries to use a particular instance,"
527  , "write that instance out with a Fail constraint."
528  , ""
529  , "For more information, see"
530  , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)."
531  ]
532
533warn :: Declaration
534warn = primClassOf (P.primSubName "TypeError") "Warn" $ T.unlines
535  [ "The Warn type class allows a custom compiler warning to be displayed."
536  , ""
537  , "For more information, see"
538  , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)."
539  ]
540
541kindDoc :: Declaration
542kindDoc = primTypeOf (P.primSubName "TypeError") "Doc" $ T.unlines
543  [ "`Doc` is the kind of type-level documents."
544  , ""
545  , "This kind is used with the `Fail` and `Warn` type classes."
546  , "Build up a `Doc` with `Text`, `Quote`, `QuoteLabel`, `Beside`, and `Above`."
547  ]
548
549textDoc :: Declaration
550textDoc = primTypeOf (P.primSubName "TypeError") "Text" $ T.unlines
551  [ "The Text type constructor makes a Doc from a Symbol"
552  , "to be used in a custom type error."
553  , ""
554  , "For more information, see"
555  , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)."
556  ]
557
558quoteDoc :: Declaration
559quoteDoc = primTypeOf (P.primSubName "TypeError") "Quote" $ T.unlines
560  [ "The Quote type constructor renders any concrete type as a Doc"
561  , "to be used in a custom type error."
562  , ""
563  , "For more information, see"
564  , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)."
565  ]
566
567quoteLabelDoc :: Declaration
568quoteLabelDoc = primTypeOf (P.primSubName "TypeError") "QuoteLabel" $ T.unlines
569  [ "The `QuoteLabel` type constructor will produce a `Doc` when given a `Symbol`. When the resulting `Doc` is rendered"
570  , "for a `Warn` or `Fail` constraint, a syntactically valid label will be produced, escaping with quotes as needed."
571  , ""
572  , "For more information, see"
573  , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)."
574  ]
575
576besideDoc :: Declaration
577besideDoc = primTypeOf (P.primSubName "TypeError") "Beside" $ T.unlines
578  [ "The Beside type constructor combines two Docs horizontally"
579  , "to be used in a custom type error."
580  , ""
581  , "For more information, see"
582  , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)."
583  ]
584
585aboveDoc :: Declaration
586aboveDoc = primTypeOf (P.primSubName "TypeError") "Above" $ T.unlines
587  [ "The Above type constructor combines two Docs vertically"
588  , "in a custom type error."
589  , ""
590  , "For more information, see"
591  , "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)."
592  ]
593