Skip to content

Experiments with Type module #118

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 158 additions & 39 deletions src/Core__Type.mjs
Original file line number Diff line number Diff line change
@@ -1,53 +1,172 @@
// Generated by ReScript, PLEASE EDIT WITH CARE

import * as Caml_option from "rescript/lib/es6/caml_option.js";

function toObjectUnsafe(i) {
return i;
}

function toBoolUnsafe(i) {
return i;
}

function toFloatUnsafe(i) {
return i;
}

function toBigIntUnsafe(i) {
return i;
}

function toStringUnsafe(i) {
return i;
}

function toSymbolUnsafe(i) {
return i;
}

function toFunctionUnsafe(i) {
return i;
}

var isNull = (function(a) { return (a===null); });

var isNullOrUndefined = (function(a) { return (a===null || a===undefined); });

var isUndefined = (function(a) { return (a===undefined); });

function classify(value) {
var match = Object.prototype.toString.call(value);
switch (match) {
case "[object Boolean]" :
return {
TAG: /* Bool */0,
_0: value
};
case "[object AsyncFunction]" :
case "[object Function]" :
case "[object GeneratorFunction]" :
return {
TAG: /* Function */4,
_0: value
};
case "[object Null]" :
return /* Null */0;
case "[object Number]" :
return {
TAG: /* Number */2,
_0: value
};
case "[object String]" :
return {
TAG: /* String */1,
_0: value
};
case "[object Symbol]" :
return {
TAG: /* Symbol */5,
_0: value
};
case "[object Undefined]" :
return /* Undefined */1;
default:
var match = typeof value;
if (match === "symbol") {
return {
TAG: /* Symbol */5,
_0: value
};
} else if (match === "boolean") {
return {
TAG: /* Bool */1,
_0: value
};
} else if (match === "string") {
return {
TAG: /* String */4,
_0: value
};
} else if (match === "function") {
return {
TAG: /* Function */6,
_0: value
};
} else if (match === "object") {
if (isNull(value)) {
return /* Null */1;
} else {
return {
TAG: /* Object */3,
TAG: /* Object */0,
_0: value
};
}
} else if (match === "undefined") {
return /* Undefined */0;
} else if (match === "number") {
return {
TAG: /* Number */2,
_0: value
};
} else {
return {
TAG: /* BigInt */3,
_0: value
};
}
}

var Classify = {
classify: classify
};
function toObject(i) {
if (typeof i === "object") {
return Caml_option.some(i);
}

}

function toBool(i) {
if (typeof i === "boolean") {
return Caml_option.some(i);
}

}

function toFloat(i) {
if (typeof i === "number") {
return Caml_option.some(i);
}

}

function toBigInt(i) {
if (typeof i === "bigint") {
return Caml_option.some(i);
}

}

function toString(i) {
if (typeof i === "string") {
return Caml_option.some(i);
}

}

function toSymbol(i) {
if (typeof i === "symbol") {
return Caml_option.some(i);
}

}

function toFunction(i) {
if (typeof i === "function") {
return Caml_option.some(i);
}

}

function get(item, name) {
if (isNullOrUndefined(item)) {
return ;
} else {
return item[name];
}
}

function getBySymbol(item, sym) {
if (isNullOrUndefined(item)) {
return ;
} else {
return item[sym];
}
}

export {
Classify ,
classify ,
isUndefined ,
isNull ,
isNullOrUndefined ,
toObject ,
toBool ,
toFloat ,
toBigInt ,
toString ,
toSymbol ,
toFunction ,
toObjectUnsafe ,
toBoolUnsafe ,
toFloatUnsafe ,
toBigIntUnsafe ,
toStringUnsafe ,
toSymbolUnsafe ,
toFunctionUnsafe ,
get ,
getBySymbol ,
}
/* No side effect */
112 changes: 72 additions & 40 deletions src/Core__Type.res
Original file line number Diff line number Diff line change
@@ -1,43 +1,75 @@
type t = [#undefined | #object | #boolean | #number | #bigint | #string | #symbol | #function]

external typeof: 'a => t = "#typeof"

module Classify = {
type function
type object
type symbol

type t =
| Bool(bool)
| Null
| Undefined
| String(string)
| Number(float)
| Object(object)
| Function(function)
| Symbol(symbol)

@val external _internalClass: 'a => string = "Object.prototype.toString.call"
external _asBool: 'a => bool = "%identity"
external _asString: 'a => string = "%identity"
external _asFloat: 'a => float = "%identity"
external _asObject: 'a => object = "%identity"
external _asFunction: 'a => function = "%identity"
external _asSymbol: 'a => symbol = "%identity"

let classify = value => {
switch _internalClass(value) {
| "[object Boolean]" => Bool(_asBool(value))
| "[object Null]" => Null
| "[object Undefined]" => Undefined
| "[object String]" => String(_asString(value))
| "[object Number]" => Number(_asFloat(value))
| "[object Function]"
| "[object GeneratorFunction]"
| "[object AsyncFunction]" =>
Function(_asFunction(value))
| "[object Symbol]" => Symbol(_asSymbol(value))
| _ => Object(_asObject(value))
type typeof = [
| #undefined
| #object
| #boolean
| #number
| #bigint
| #string
| #symbol
| #function
]

external typeof: 'a => typeof = "#typeof"

type unknown
external toUnknown: 'a => unknown = "%identity"

type object // How to use with the Object module?
type function

// typeof null == "object". Gotta love that! Do better here.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#typeof_null
type jsType =
| Undefined
| Null
| Object(object)
| Bool(bool)
| Number(float)
| BigInt(Core__BigInt.t)
| String(string)
| Symbol(Core__Symbol.t)
| Function(function)

let toObjectUnsafe = i => i->Obj.magic
let toBoolUnsafe = i => i->Obj.magic
let toFloatUnsafe = i => i->Obj.magic
let toBigIntUnsafe = i => i->Obj.magic
let toStringUnsafe = i => i->Obj.magic
let toSymbolUnsafe = i => i->Obj.magic
let toFunctionUnsafe = i => i->Obj.magic
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, this isn't what I meant. These are now all just aliases of Obj.magic, and wherever these are used internally you can just substitute with Obj.magic directly.

If you want to export them, then the externals are better because the implementation won't be hidden by the interface and consequently add a function call on every use to what should be a no-op at runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the problem. I looked at the javascript and there are all these wrappers and we want it to be completely compiled away. I went back to using external %identity in both the resi and the res because that results in less Js output. I find it very weird that the resi file has implementation details like external. If the resi just says let toBoolUnsafe: 'a => bool then this is the output...

function toBoolUnsafe(prim) {
  return prim;
}

In a perfect world wouldn't you be able to only use external in the implementation?


let isNull = %raw(`function(a) { return (a===null); }`)
let isNullOrUndefined = %raw(`function(a) { return (a===null || a===undefined); }`)
let isUndefined = %raw(`function(a) { return (a===undefined); }`)

let classify = value => {
switch typeof(value) {
| #number => value->toFloatUnsafe->Number
| #string => value->toStringUnsafe->String
| #boolean => value->toBoolUnsafe->Bool
| #undefined => Undefined
| #function => value->toFunctionUnsafe->Function
| #bigint => value->toBigIntUnsafe->BigInt
| #symbol => value->toSymbolUnsafe->Symbol
| #object =>
switch isNull(value) {
| true => Null
| false => value->toObjectUnsafe->Object
}
}
}

let toObject = i => typeof(i) === #object ? i->Obj.magic->Some : None
let toBool = i => typeof(i) === #boolean ? i->Obj.magic->Some : None
let toFloat = i => typeof(i) === #number ? i->Obj.magic->Some : None
let toBigInt = i => typeof(i) === #bigint ? i->Obj.magic->Some : None
let toString = i => typeof(i) === #string ? i->Obj.magic->Some : None
let toSymbol = i => typeof(i) === #symbol ? i->Obj.magic->Some : None
let toFunction = i => typeof(i) === #function ? i->Obj.magic->Some : None

// throws on null or undefined
@get_index external getUnsafe: ('a, string) => option<unknown> = ""
@get_index external getBySymbolUnsafe: ('a, Core__Symbol.t) => option<unknown> = ""

let get = (item, name) => isNullOrUndefined(item) ? None : item->getUnsafe(name)
let getBySymbol = (item, sym) => isNullOrUndefined(item) ? None : item->getBySymbolUnsafe(sym)
Loading