While this may not directly answer your question, it could offer a solution to the issue at hand
In JavaScript, multiple inheritance is not supported. However, functional programming in no way relies on JavaScript's class system, object prototypes, or other object-oriented concepts. Functional programming revolves around functions!
To create inheritable modules, we start by defining some functions
// module 1
const hasFirstName = (firstName = "") => o =>
{
field (o, 'firstName', firstName)
}
// module 2
const hasMiddleName = (middleName = "") => o =>
{
field (o, 'middleName', middleName)
}
// module 3
const hasLastName = (lastName = "") => o =>
{
field (o, 'lastName', lastName)
}
We haven't defined field
yet, but let's explore a more complex module next
// module 4
const nameable = (first, middle, last) => o =>
{
inherit (o, hasFirstName (first))
inherit (o, hasMiddleName (middle))
inherit (o, hasLastName (last))
method (o, 'getFullName', (self) => `${self.firstName} ${self.middleName} ${self.lastName}`)
method (o, 'sayHelloTo', (self, other) => `Hello ${other.getFullName ()}, my name is ${self.getFullName ()}`)
}
This showcases how modules can be composed of one another. Before diving into inherit
and method
, let's see how our module would be used
const Person = (first, middle, last) =>
Obj (self => {
inherit (self, nameable (first, middle, last))
})
It might seem like I'm crafting new code with each example, but this "wishful thinking" technique is potent
"Wishful Thinking" is a powerful programming strategy:
Prior to implementing a component, write some code that actually utilizes it. This approach helps identify the necessary functions and parameters for an efficient interface. It also serves as good test code for the component.
The concept emphasizes that an interface should simplify the code using the component, not the code implementing it.
By employing this strategy, we constructed an imaginary object system based on desired functionality – not limited by JavaScript's native capabilities
Expect working with our Person
to be straightforward
const p1 =
Person ('Augusta', 'Ada', 'King-Noel', 166)
const p2 =
Person ('Gerald', 'Jay', 'Sussman', 71)
console.log (p1.sayHelloTo (p2))
// Hello Gerald Jay Sussman, my name is Augusta Ada King-Noel
The beauty here lies in the absence of class
or this
. Even without JavaScript's built-in object system, you can forge your own
const Obj = (f, instance = {}) =>
(f (instance), instance)
const inherit = (o, f) =>
Object.assign (o, f (o))
const field = (o, name, value) =>
Object.assign (o, { [name]: value })
const method = (o, name, f) =>
Object.assign (o, { [name]: (...xs) => f (o, ...xs) })
Full program demonstration
// DIY class system
const Obj = (f, instance = {}) =>
(f (instance), instance)
const inherit = (o, f) =>
Object.assign (o, f (o))
const field = (o, name, value) =>
Object.assign (o, { [name]: value })
const method = (o, name, f) =>
Object.assign (o, { [name]: (...xs) => f (o, ...xs) })
// module 1
const hasFirstName = (firstName = "") => o =>
{
field (o, 'firstName', firstName)
}
// module 2
const hasMiddleName = (middleName = "") => o =>
{
field (o, 'middleName', middleName)
}
// module 3
const hasLastName = (lastName = "") => o =>
{
field (o, 'lastName', lastName)
}
// module 4
const nameable = (first, middle, last) => o =>
{
inherit (o, hasFirstName (first))
inherit (o, hasMiddleName (middle))
inherit (o, hasLastName (last))
method (o, 'getFullName', (self) => `${self.firstName} ${self.middleName} ${self.lastName}`)
method (o, 'sayHelloTo', (self, other) => `Hello ${other.getFullName ()}, my name is ${self.getFullName ()}`)
}
// Person class
const Person = (first, middle, last) =>
Obj (self => {
inherit (self, nameable (first, middle, last))
})
// demo
const p1 =
Person ('Augusta', 'Ada', 'King-Noel')
const p2 =
Person ('Gerald', 'Jay', 'Sussman')
console.log (p1.sayHelloTo (p2))
// Hello Gerald Jay Sussman, my name is Augusta Ada King-Noel
Our Person class can obviously define its own fields and methods as well
const dateDiff = (d1, d2) =>
Math.abs (d1 - d2) / 1000 / 60 / 60 / 24 / 365 >> 0
const Person = (first, middle, last, birthdate = new Date) =>
Obj (self => {
inherit (self, nameable (first, middle, last))
field (self, 'birthdate', birthdate)
method (self, 'calculateAge', (self) => dateDiff (new Date, self.birthdate))
method (self, 'sayAge', (self) => `I am ${self.calculateAge()} years old`)
})
const p2 =
Person ('Gerald', 'Jay', 'Sussman', new Date ('February 8, 1947'))
console.log (p2.sayAge ())
// I am 71 years old
Get creative and invent any other features you want
- Consider adding an
overrideMethod
to define a new method with the same name while retaining access to both
- Explore possibilities like
privateMethod
or classMethod
helpers
- Possibly enhance
field
to emit events when values change
- Contemplate restricting value setting with
mutableField
for changeable fields
Design it according to your preferences and bring your ideas to life. Your only limitations are those you impose upon yourself