Skip to content

Expose Python type references and dict/list constructors in Builtins #290

@dbrattli

Description

@dbrattli

Summary

Fable.Python.Builtins currently exposes int, float, str, len, isinstance, etc. as methods on the IExports interface. This is great for calling builtins.int(x) etc., but for isinstance checks the Python type values themselves are also needed — and they aren't currently exposed. Three small additions would let downstream libraries drop a recurring chunk of [<Emit>] boilerplate.

What's missing

1. Type references as values

To use the idiomatic Fable.Core.PyInterop.pyInstanceof, the caller needs a value reference to each Python type:

pyInstanceof v pyInt   // compiles to isinstance(v, int)

Today every consumer redeclares them privately. Both Thoth.Json.Python and Fable.TypedJson do exactly this:

[<Global; Emit("int")>]   let pyInt:   obj = nativeOnly
[<Global; Emit("float")>] let pyFloat: obj = nativeOnly
[<Global; Emit("bool")>]  let pyBool:  obj = nativeOnly
[<Global; Emit("str")>]   let pyStr:   obj = nativeOnly
[<Global; Emit("list")>]  let pyList:  obj = nativeOnly
[<Global; Emit("dict")>]  let pyDict:  obj = nativeOnly

Suggested home: a new Fable.Python.Types module, or top-level let bindings under Fable.Python.Builtins.

2. The None singleton

json.dumps, JSON-null sentinels, default values, and many other interop sites need a value reference to Python's None:

[<Global; Emit("None")>] let pyNone: obj = nativeOnly

3. dict() / list(...) constructors on IExports

Builtins.IExports already has int, float, str constructors (abstract int: obj -> int, etc.). The two collection constructors are missing:

abstract dict: unit -> obj
abstract dict: IEnumerable<string * obj> -> obj
abstract list: IEnumerable<'T> -> obj

Today consumers fall back to [<Emit("dict()")>] / [<Emit("list($0)")>] even though the type values exist as Python builtins.

Why it's worth shipping

Once these land, libraries that wrap Python interop can drop the boilerplate and use:

open Fable.Core.PyInterop
open Fable.Python.Builtins

// type tests
pyInstanceof v pyInt    // was: [<Emit("isinstance($0, int)")>]
pyInstanceof v pyDict   // was: [<Emit("isinstance($0, dict)")>]

// constructors
builtins.dict ()        // was: [<Emit("dict()")>]
builtins.list items     // was: [<Emit("list($0)")>]

// null
pyNone                  // was: [<Emit("None")>]

Each of these is a one-liner today, but every Fable Python library reinvents the same set. Centralizing them in Fable.Python.Builtins (or a small Fable.Python.Types module) keeps consumer code clean and matches Fable's existing pattern of providing first-class wrappers for everything the Python stdlib offers.

Reference

Concrete consumer that currently has these as private declarations:

Happy to put up a PR if the proposed shape looks right.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions