not use path based imports
not use path based imports is very important.
Node, Deno are running on the wrong way, on the other hand, Carogo/rust do it right.
Why do I emphasize this ? Let's see:
// package: ffmpeg
// file: ffmpeg/utils.js
export const a = xxx;
export const b = xxx;
// file: ffmpeg/index.js
import { a, b } from './utils';
export const encode = xxx;
export const decode = xxx;
// As a package, I want to make just `encode, decode` public, but `a, b` private to other users.
// But in JS, I can't do it. People may just:
import { a, b } from 'ffmpeg/utils';
// You must don't want it
What if we change JS to:
// package user can just import package index file
import * as ffmpeg from 'ffmpeg';
// they can't import other files in that package
import * as utils from 'ffmpeg/utils';
// this should be syntax error
// what if I want to make `ffmpeg/utils` public
// file: ffmpeg/index.js
export * as utils from './utils';
export const encode = xxx;
export const decode = xxx;
// the people can
import { utils } from 'ffmpeg'
So, JUST MAKE THIS SYNTAX ERROR: import * as utils from 'ffmpeg/utils';
Well, we can't change JS, but we should do Koka the right way.
Well, it's just like Java's public protected private, the last problem is "should it default private or protected". I prefer protected in Koka.
If it's default protected, then
// package: ffmpeg
// file: ffmpeg/src/utils.kk
fun a() xxx
fun b() xxx
pub fun c() xxx
// file: ffmpeg/src/lib.kk
pub fun encode()
utils.a() // just use utils here
// if want to make utils public
pub utils
// package: my_app
// file: my_app/src/main.kk
use ffmpeg
// or
use ffmpeg.{utils, encode}
fun main()
utils.c()
Originally posted by @xialvjun in https://github.com/koka-lang/koka/issues/31#issuecomment-1482200826
reference: https://api-extractor.com/pages/setup/configure_rollup/#:~:text=(The%20API%20Extractor,with%20that%20effort.)
@daanx Hope you fix this before it's too late.
Surely a better way to fix this is just to have the code explicitly set the scope of public/private? For example in Cargo you can do pub(crate) to restrict access to that crate.
Don't forget the enormous benefits that come from using path based imports:
- It's way simpler for users to understand.
- It's way simpler for IDEs to deal with - they can literally just open the file.
- It requires less setup and makes ad hoc scripting tractable (Deno is very good for this for example).
You can see the kind of disaster that you end up with if the import system gets too "magical" in Python.
I don't know why "no path based imports" may be a disaster.
Things should be simple. We can access:
sibling
sibling's public children
sibling's public children's public children...
ancestor
ancestor's public children
ancestor's public children's public children...
ancestor's sibling
ancestor's sibling's public children
ancestor's sibling's public children's public children
or short version
sibling
ancestor
ancestor's sibling
and use dot operator to access the public children of everything you can access
So, an example:
pac: package
pri: private - default
pub: public
use: we can access these things here
pac A
pri B
pri C
use C, B, B.D, A, A.E, A.E.G, D (ie C,B,A,D and their recursive pubic children)
pub D
use D, B, B.D, A, A.E, A.E.G, C (ie D,B,A,C and their recursive pubic children)
use C, D, B, B.D, A, A.E, A.E.G (ie C,D,B,A and their recursive pubic children)
pub E
pri F
use F, E, E.G, A, A.E, A.E.G, G (ie F,E,A,G and their recursive pubic children)
pub G
use G, E, E.G, A, A.E, A.E.G, F (ie G,E,A,F and their recursive pubic children)
use F, G, E, E.G, A, A.E, A.E.G (ie F,G,E,A and their recursive pubic children)
use B, B.D, E, E.G, A, A.E, A.E.G (ie B,E,A and their recursive pubic children)
It seems use and mod is just sugar of const, and dependency list is just top module
// Cargo.toml
[dependency]
tokio=0.1.0
hyper=0.2.0
// In src/main.rs
// dependency list is just top module, it's just like put the content of the crate in src/main.rs
mod tokio {
// tokio's content
}
mod hyper {xxx}
pub fn main() {}
use hyper::http;
// is the same as
const {http} = hyper;
// if you want to define a module with the same name of one of your dependency list, just shadow the name
const real_tokio = tokio;
mod tokio { xxx };
// module can shadow the name, so it's just like const. mod is just a sugar
mod abc { pub fn a() {} }
const abc = { fn a() {}; { a } }
// even we define some compile time items like type in module, because const is also compile time
mod abc { pub type a {} }
const abc = { type a {}; { a } }
I guess the request is for something like protected? If you give a Koka file a module declaration by default all of its functions are private unless explicitly marked pub. This goes for both files relative to the module (inside the library) as well as external users. Are you arguing that without something like protected (package private) we might end up making too many things public?
I don't see what that issue has to do with referring to files by path based imports. If it is private or protected (package private) then it shouldn't matter if you know the path to it, you cannot access the definition. If JavaScript messed that up it doesn't have to be the same for Koka regardless whether Koka uses paths or not.
My question in short: how to declare something public in its library but private out of its library.
Yeah, there is no current mechanism for that in Koka, other than to include the private definitions in the library you want to use them in - though this makes it hard to share across internal files, or use a naming convention that will make it clear to external users not to use it internal- etc. I'm not sure if Daan has plans for some sort of mechanism like this. If you want users to be able to see a type, but not be able to construct / match on the type you can use the keyword abstract, which forces them to interact with it via functions you define (since they cannot inspect it at all).
Personally I would go with the naming convention. I'm not a huge fan of protected/private in general. If a user wants to use pieces of your library to create their own library that fits their use case better they have to fork it and try to keep the fork synced and up to date. A naming convention would make it clear to most users which functions are intended for internal use only, and only expert users who understand the internal mechanisms should use. Additionally I would create a top level import mypackage which includes only the necessary interfaces / functions or pub exports some other files that contain only the necessary interfaces, and put all the implementation under an internal or impl directory. This makes it clear when a user is using internal or implementation details, while encouraging them to use just the single import.
I understand that many people will not feel the same way as me, just putting out my opinion.
Some use case: I have a library like ffmpeg for av processing, and I need some tool functions in it, those tool functions are only used by myself in this library, I won't ensure their api stability, so they shouldn't be exposed to library users.
Well, explicit name internal- did solve problem, but explicit effect name in function function a_function_use_io() {} also solve effect type, then why we need algebraic effect.
Mean no offend, I just want to say: we should not trust human consciousness.
EDIT: Sorry, I found explicit name internal- and explicit effect name in function function a_function_use_io() {}, they do have differences. We do set explicit item name internal_, but we didn't set explicit effect name _use_io. That's the difference. I just imagined a problem which doesn't exist.
@TimWhiting I noticed there at https://github.com/koka-community/html/blob/b59d465bb8813a97e36f7310885a5e8f8ff1b893/html/html.kk#L9-L10
pub import html-builder
pub import core-components
Non-pub artifacts within html/html-builder and html/core-components seem to have been publicly re-exported from html/html, I'd imagine only pub artifacts get re-exported in this way, is current result a bug or right by design?
Only the pub functions should be re-exported and available. If that is not the case we should create a new issue for this.