Open kee-oth opened 2 years ago
Since TypL already uses `template literals`
to embed custom syntax, I am wondering/hoping maybe we could figure out a way to piggyback additional comments directly in the type annotations.
For example:
isChecked = bool`${obj.someFlag} : a comment about this variable`;
// or...
isChecked = bool`${obj.someFlag}${ "a comment about this variable" }`;
// or...
isChecked = bool`${obj.someFlag}${{
description: "a comment about this variable"
}}`
I can't but help notice the following when I see this pattern, and it intrigues me.
Maybe my idea would be better suited in a different project, but I'll leave it here since this inspired me.
I'm curious, if w/ functional comments, if the function signature / body could be added in there as well, where whatever was running the "documentation" function would return the execution function directly, and use the other variables as properties to send to whatever JS documentation utility of choice, as well as help construct some testing templates in w/ the test runner of choice.
Some configuration tool for the "documentation" runner could provide callback handlers for testing output, doc generation, so this stuff could be plugged into other, existing, utilities.
You could configure it to do runtime and / or compile time checking, etc. (it shouldn't even require a compilation step, just to run it).
// Where "def" is the some collective function definer w/ potential lifecycle hooks, etc.
const updateOrganization = def ('updateOrganization', {
description: 'This function updates an organization',
parameters: {
// Note: My examples are bad here, but the goal would be to be able to use
// "native" types directly along w/ comments... this isn't how it would actually
// look in code, though
organizationId: String,
organizations: Object[],
updateProperties: 'the properties to update an organization with',
},
returns: 'the updated organizations',
// "signature" could be renamed "body", "exec" or something (the actual function to run)
signature: (....) => {
// ... do cool stuff
}
})
Call it like so
updateOrganization(organizationId, organizations, updateProperties) // ... do cool stuff
@getify
Pros
1) Developers don't have to import documentation
function
2) Better for quick documentation
3) Clearly a TypL enhancement
Cons
1) I worry that would get a bit unwieldy when used around the app
2) How would it work with documenting function parameters without default values?
3) Documentation is fragmented. To get the full idea of the function documentation you'd have a scan a few different places and put it all together in your head.
4) Limits spinning off documentation
as a standalone project
I think having both APIs: function-based and inline would be great. It makes "two ways to do something" but the two different ways are different enough to not cause much confusion I think.
@jzombie
I actually had the same idea! I can see myself enjoying this but I think it's "weird" so it might hurt developer adoption if this was the only way. And I imagine it'd be hard to "strip" the documentation stuff out when minifying a project. I figure it'd be easier to just get rid of documentation
calls vs separating it out. But maybe @getify actually knows how difficult that would be.
Another option that would make exporting/importing documention pretty easy. We could curry the documentation function if the function isn't supplied:
// commonDictionary.docs.js
export const organizationId = string`the id for an organization`
export const organizations = string`a list of organizations`
// organizations.docs.js
import { organizationId, organizations } from './commonDictionary.docs.js'
const deleteOrganization = documentation ({
description: 'This function removes an organization',
parameters: {
organizationId,
organizations,
},
returns: 'the updated organizations',
})
const updateOrganization = documentation ({
description: 'This function updates an organization',
parameters: {
organizationId,
organizations,
updateProperties: 'the properties to update an organization with',
},
returns: 'the updated organizations',
})
export default {
deleteOrganization,
updateOrganization,
}
// organizations.js
import organizationDocs from './organizations.docs.js'
const deleteOrganization = (organizationId = string, organizations = array) => {
return organizations.filter (organization => organization.id !== organizationId)
}
organizationDocs.deleteOrganization(deleteOrganization)
const updateOrganization = (organizationId = string, organizations = array, updateProperties = object) => {
return organizations.map (organization => {
return organization.id === organizationId
? { organization, ...updateProperties }
: organization
})
}
organizationDocs.updateOrganization(updateOrganization)
The above idea also makes sharing definitions really easy:
// commonDictionary.docs.js
export const organizationId = string`the id for an organization`
export const organizations = string`a list of organizations`
// organizations.docs.js
import { organizationId, organizations } from './commonDictionary.docs.js'
const manageOrganizations = documentation ({
parameters: {
organizationId,
organizations,
},
returns: 'the updated organization',
})
export default {
manageOrganizations,
}
// organization.js
import { manageOrganizations } from './organizations.docs.js'
const deleteOrganization = (organizationId = string, organizations = array) => {
return organizations.filter (organization => organization.id !== organizationId)
}
manageOrganizations(deleteOrganization, {
description: 'This function removes an organization',
})
const updateOrganization = (organizationId = string, organizations = array, updateProperties = object) => {
return organizations.map (organization => {
return organization.id === organizationId
? { organization, ...updateProperties }
: organization
})
}
manageOrganizations(updateOrganization, {
parameters: {
updateProperties: 'the properties to update an organization with', // adds to parameters, doesn't overwrite
},
description: 'This function updates an organization',
})
It does fragment the documentation which isn't great imo but so long as we have good IDE support which tooltips that put everything together dynamically I think we could get away with it.
@kee-oth I have limited exposure to the inner-workings of the ASTs and whatnot during the compilation process, but I'd think a rudimentary compilation step could be replacing the function documentor directly with the return function possibly without even currying it.
(this being for the documentation function w/ the built-in exec handler)
I believe TypL could benefit from implementing documentation functionality.
Types are useful for documentation but they're only a partial strategy. If a developer were to use TypL for a project and also wanted documentation they'd likely reach for JSDoc and omit type information. JSDoc is widespread but it's an old, inactive project with clunky, generally disliked syntax (I only have anecdotal evidence for this claim).
I propose TypL could enter this field to 1) give developers a modern documentation system and 2) enhance TypL itself with more detailed runtime error descriptions. This would both bring more attention to TypL itself and increase the value of and use cases for TypL.
Nice IDE integration for JSDoc-like tooltips would be a big bonus too.
Usage example with rough API:
Resulting runtime error:
"Function Parameters" above is where we see the mixture of TypL's type information and the user-generated documentation. I'm sure we could come up with other benefits of mixing type information and documentation as well.
documentation
could be configurable to spit out more or less information. For example: a developer could configure the output to includedescription
andreturns
, unlike what you see above.Benefits of a function-based API (as opposed to JSDoc style comments):
Basically, it's JS, do what you want to.
Simple example showing how an application could have a common dictionary of terms to be used around the codebase