rescript-lang / rescript-lang/rescript-vscode

docgen does not distinguish between module and functor

Open
#1,012 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
ReScript
Stars
354
Forks
63
Avg merge
11h 29m
Merged PRs (30d)
1

Description

Currently a functor is treated exactly like a module (but ignores existent docstrings).

Example Functor to test docgen
/***
this module provides "convenient" utilities for logging  
NOTE: this is not a good example of a functor,
    but just to test docgen for functors
*/

module type Input = {
  /***
  input module type of the functor
  */

  /**
  type of the input
  */
  type t
  /**
  converts the input to a string
  */
  let toString: t => string
}

module type Logger = {
  /***
  returned type of the functor
  */

  /**
  type of the input
  */
  type t
  /**
  logs the input
  */
  let log: t => unit
}

module String = {
  /***
  input module of the functor to hanle strings
  */

  /**
  type of the input
  */
  type t = string

  /**
  converts the input to a string
  */
  let toString = (x: t) => x
}

module Int = {
  /***
  input module of the functor to handle integers
  */

  /**
  type of the input
  */
  type t = int

  /**
  converts the input to a string
  */
  let toString = (x: t) => x->Int.toString
}

/**
 functor that logs the input
 */
module MakeLogger = (I: Input): (Logger with type t := I.t) => {
  /***
  functor that logs the input
  */

  /**
   logs the Input
   */
  let log = (x: I.t) => I.toString(x)->Console.log
}

/**
the module that contains the functions to log strings
*/
module StringLogger = MakeLogger(String)
/**
the module that contains the functions to log integers
*/
module IntLogger = MakeLogger(Int)

module Var = {
  /***
  another input module for the Logger functor to handle a Variant of type type 
  */

  /**
  type of the input
  */
  type t = Yes | No

  /**
  converts the input to a string
  */
  let toString = x =>
    switch x {
    | Yes => "Yes"
    | No => "No"
    }
}

module Included = {
  /***
  module which includes the Var Logger!
  */
  include MakeLogger(Var)
}
generated json
{
  "name": "Functor",
  "docstrings": ["this module provides \"convenient\" utilities for logging  \nNOTE: this is not a good example of a functor,\n    but just to test docgen for functors"],
  "source": {
    "filepath": "test/Functor.res",
    "line": 1,
    "col": 1
  },
  "items": [
  {
    "id": "Functor.Input",
    "name": "Input",
    "kind": "moduleType",
    "docstrings": [],
    "source": {
      "filepath": "test/Functor.res",
      "line": 7,
      "col": 13
    },
    "items": [
    {
      "id": "Functor.Input.t",
      "kind": "type",
      "name": "t",
      "signature": "type t",
      "docstrings": ["type of the input"],
      "source": {
        "filepath": "test/Functor.res",
        "line": 15,
        "col": 3
      }
    }, 
    {
      "id": "Functor.Input.toString",
      "kind": "value",
      "name": "toString",
      "signature": "let toString: t => string",
      "docstrings": ["converts the input to a string"],
      "source": {
        "filepath": "test/Functor.res",
        "line": 16,
        "col": 3
      }
    }]
  }, 
  {
    "id": "Functor.Logger",
    "name": "Logger",
    "kind": "moduleType",
    "docstrings": [],
    "source": {
      "filepath": "test/Functor.res",
      "line": 22,
      "col": 13
    },
    "items": [
    {
      "id": "Functor.Logger.t",
      "kind": "type",
      "name": "t",
      "signature": "type t",
      "docstrings": ["type of the input"],
      "source": {
        "filepath": "test/Functor.res",
        "line": 30,
        "col": 3
      }
    }, 
    {
      "id": "Functor.Logger.log",
      "kind": "value",
      "name": "log",
      "signature": "let log: t => unit",
      "docstrings": ["logs the input"],
      "source": {
        "filepath": "test/Functor.res",
        "line": 31,
        "col": 3
      }
    }]
  }, 
  {
    "id": "Functor.String",
    "name": "String",
    "kind": "module",
    "docstrings": ["input module of the functor to hanle strings"],
    "source": {
      "filepath": "test/Functor.res",
      "line": 37,
      "col": 8
    },
    "items": [
    {
      "id": "Functor.String.t",
      "kind": "type",
      "name": "t",
      "signature": "type t = string",
      "docstrings": ["type of the input"],
      "source": {
        "filepath": "test/Functor.res",
        "line": 45,
        "col": 3
      }
    }, 
    {
      "id": "Functor.String.toString",
      "kind": "value",
      "name": "toString",
      "signature": "let toString: t => t",
      "docstrings": ["converts the input to a string"],
      "source": {
        "filepath": "test/Functor.res",
        "line": 50,
        "col": 7
      }
    }]
  }, 
  {
    "id": "Functor.Int",
    "name": "Int",
    "kind": "module",
    "docstrings": ["input module of the functor to handle integers"],
    "source": {
      "filepath": "test/Functor.res",
      "line": 53,
      "col": 8
    },
    "items": [
    {
      "id": "Functor.Int.t",
      "kind": "type",
      "name": "t",
      "signature": "type t = int",
      "docstrings": ["type of the input"],
      "source": {
        "filepath": "test/Functor.res",
        "line": 61,
        "col": 3
      }
    }, 
    {
      "id": "Functor.Int.toString",
      "kind": "value",
      "name": "toString",
      "signature": "let toString: t => string",
      "docstrings": ["converts the input to a string"],
      "source": {
        "filepath": "test/Functor.res",
        "line": 66,
        "col": 7
      }
    }]
  }, 
  {
    "id": "Functor.MakeLogger",  <-- this is the functor
    "name": "MakeLogger",
    "kind": "module",
    "docstrings": [],  <-- empty docstrings
    "source": {
      "filepath": "test/Functor.res",
      "line": 1,
      "col": 1
    },
    "items": [
    {
      "id": "Functor.MakeLogger.log",
      "kind": "value",
      "name": "log",
      "signature": "let log: I.t => unit",
      "docstrings": ["logs the input"],
      "source": {
        "filepath": "test/Functor.res",
        "line": 31,
        "col": 3
      }
    }]
  }, 
  {
    "id": "Functor.StringLogger",
    "kind": "moduleAlias",
    "name": "StringLogger",
    "docstrings": ["the module that contains the functions to log strings"],
    "source": {
      "filepath": "test/Functor.res",
      "line": 86,
      "col": 8
    },
    "items": []
  }, 
  {
    "id": "Functor.IntLogger",
    "kind": "moduleAlias",
    "name": "IntLogger",
    "docstrings": ["the module that contains the functions to log integers"],
    "source": {
      "filepath": "test/Functor.res",
      "line": 90,
      "col": 8
    },
    "items": []
  }, 
  {
    "id": "Functor.Var",
    "name": "Var",
    "kind": "module",
    "docstrings": ["another input module for the Logger functor to handle a Variant of type type"],
    "source": {
      "filepath": "test/Functor.res",
      "line": 92,
      "col": 8
    },
    "items": [
    {
      "id": "Functor.Var.t",
      "kind": "type",
      "name": "t",
      "signature": "type t = Yes | No",
      "docstrings": ["type of the input"],
      "source": {
        "filepath": "test/Functor.res",
        "line": 100,
        "col": 3
      },
      "detail": 
      {
        "kind": "variant",
        "items": [
        {
          "name": "Yes",
          "docstrings": [],
          "signature": "Yes"
        }, 
        {
          "name": "No",
          "docstrings": [],
          "signature": "No"
        }]
      }
    }, 
    {
      "id": "Functor.Var.toString",
      "kind": "value",
      "name": "toString",
      "signature": "let toString: t => string",
      "docstrings": ["converts the input to a string"],
      "source": {
        "filepath": "test/Functor.res",
        "line": 105,
        "col": 7
      }
    }]
  }, 
  {
    "id": "Functor.Included",
    "name": "Included",
    "kind": "module",
    "docstrings": ["module which includes the Var Logger!"],
    "source": {
      "filepath": "test/Functor.res",
      "line": 112,
      "col": 8
    },
    "items": [
    {
      "id": "Functor.Included.log",
      "kind": "value",
      "name": "log",
      "signature": "let log: Var.t => unit",
      "docstrings": ["logs the input"],
      "source": {
        "filepath": "test/Functor.res",
        "line": 31,
        "col": 3
      }
    }]
  }]
}

I guess we need to introduce a new kind called functor?
At least a "signature" field should be present (similar to items of kind value): "signature": "module MakeLogger = (I: Input) => (Logger with type t := I.t)". And docstrings should be taken into account.

Questions
  • What to show as return type for a functor with no explicit return type annotation?
  • How to handle (destructive) substitution (with type...:= / = e.g. Logger with type t := I.t)? Is there something to do at all?
  • How to make referenced module types machine readable? (should there be a json field with an array of referenced module type ids? (How) Is this related to #1010?)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the functor example in test/Functor.res and compare its generated JSON, especially MakeLogger, with the requested metadata. Trace the docgen handling for modules and functors, then define and test the JSON representation, signature, docstrings, return type, substitutions, and referenced module types.

Written by the indexing model from the issue text.

Assessment

Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.