2

I'm having this code in JavaScript

class StringFormatter {

 init(locale) {
  this.locale = locale;
 }

 getA() {
  return 'A' + this.locale
 }

 getB() {
  return 'B' + this.locale
 }

 getC() {
  return 'C' + this.locale
 }

}


// when page just render
let stringManager = new StringFormatter();
stringManager.init('enUS');
// call it in other components 
let result = stringManager.getA();

so when I'm using this OOP class, I only need to set the locale once by init it when page first render. And then all of the later code where I call methods getA, getB or getC will adapt to this locale.

Now as I want to move this to a more Functional Programming way, how can I write it but still keep the same concept. Right now I can only come up with this solution

 getA(locale) {
  return 'A' + locale
 }

 getB(locale) {
  return 'B' + locale
 }

 getC(locale) {
  return 'C' + locale
 }

let result = getA('enUS');

But this one required me to keep passing the locale every time I call the function getA, getB or getC.

Is there anyway that we somehow still can config the locale but only once, don't need to pass it every time to getA, getB or getC function, and don't need to write in OOP way?

5
  • If you want to chain, you will need an object, or you can make a default value. Commented Feb 2, 2021 at 16:16
  • That's the main characteristic of FP. A function doesn't change global variables and doesn't have a state. You can use global variables or some kind of state but that's not FP. So the answer is: No. Commented Feb 2, 2021 at 16:20
  • What's wrong with passing locale to getA, getB, and getC every time? You're doing the same thing with OOP too. OOP forces you to write stringManager.getA(), stringManager.getB(), and stringManager.getC(). How is that better than writing getA(locale), getB(locale), and getC(locale)? Commented Feb 3, 2021 at 9:13
  • @AaditMShah because in the future, the config may be even more complicated, not only locale. Then this will be the problem Commented Feb 3, 2021 at 9:33
  • That makes no difference. The inputs to getA, getB, and getC can be as complex as required. For example, instead of passing a simple string locale you can pass a complex object to these functions. I still don't see the “problem” here. Commented Feb 3, 2021 at 10:31

5 Answers 5

3

Use a closure:

let getA;
let getB;
let getC;

function initStringFormatter (x) {
    let locale = x;

    getA = function () { return 'A' + locale }
    getB = function () { return 'B' + locale }
    getC = function () { return 'C' + locale }
}

initStringFormatter('enUS');

getA();

In theory, closures can provide exactly the same features as object properties/variables/members in OOP. There's an exact one-to-one relation between closures and objects. The only difference is that closures use scope as the mechanism to attach variables to functions whereas objects use bindings to attach variables to methods.

Of course, there's nothing wrong mixing OOP with FP. Even Lisp has an OOP library. So a common javascript idiom is to return an object instead of having the function names as global variables:

function initStringFormatter (locale) { // another trick is to just use
                                        // the argument directly instead of
                                        // creating another variable.

    function a () { return 'A' + locale }
    function b () { return 'B' + locale }
    function c () { return 'C' + locale }

    return {
        getA: a,
        getB: b,
        getC: c
    }
}

let stringManager = initStringFormatter('enUS');

stringManager.getA();

Indeed. It is common to see FP programmers in javascript use objects this way: simply as namespaces for functions. State management can be done 100% using closures instead of object properties.

Side note: FP is powerful enough that languages like Lisp don't need OOP to be built-in to the language, instead OOP is a design pattern and/or a library - the standard OOP library for Lisp is CLOS: the Common Lisp Object System

The only downside of using closures is that all closures are essentially private (technically they're not private, they're just local variables to the functions - private/public are binding concepts global/local are scope concepts). But if you've done any significant amount of OOP you would recognize this as good practice in the OOP world to not ever give public access to variables. When using closures 100% of access to the variable need to be done via functions - again, in the OOP world this would be the common getter/setter design pattern which is considered good practice.

Note however this is not 100% pure FP. But that's OK. There are other FP languages that are not pure. Javascript still allows enclosed variables to be modified. However the above function is pure FP since the locale variable cannot be modified.

To make javascript 100% pure FP is simple. Simply ban all uses of let and var in your code and always use const. It feels odd at first but you can write any program using only const and function arguments (which are constants by default). Languages like javascript and Go gives you an escape to use variables to manage state based logic in a straightforward manner.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for your input, i think your closure method is the way i'm looking for. However may i ask for my javascript case, should i change to closure or just stick with the existing code in OOP? And also Im quite curious for the last paragraph you mentioned can remove let and only use const. Then if that is the case , how do you change the value of a variable ?
You don't change the value of variables. That's the whole point behind pure functional programming. That's why normal languages like C# and Java are busy implementing the immutable stuff. What you end up with is instead of changing the value of a variable you create a new function with a variable with the new value. There are whole theories and design patterns behind this the biggest of which is Monads: en.wikipedia.org/wiki/Monad_(functional_programming). It took me a long time to understand monads until I heard Douglas Crockford describe them as just a pattern of using callbacks
Like I mentioned above. The moment you remove variables from your programming language then your compiler is free to execute functions in any order. This is sometimes called lazy evaluation. Once a compiler is free to do that it can automatically execute functions in parallel. Part of the reason normal languages are embracing immutables is that without variables your program becomes much easier to understand - leading to less bugs. But part of the reason is potential future development of automatic multithreaded frameworks like the parallel Lisp I mentioned
Oh, for your first question. My personal rule is if it isn't broken then don't fix it. I'd stay with the original code unless it's broken. And I consider unmaintainable/unreadable code broken. As long as you and others can understand it and it's easy to fix bugs in the code don't change it.
If you use a closure doesn't it make getA , getB, getC untestable?
3

You might want a thunk?

A thunk delays a calculation until its result is needed, providing lazy evaluation of arguments. (ramda's thunk defination)

const thunkGetFromLocale = (locale) => () => (toGet) => toGet + locale

let thunk = thunkGetFromLocale('en-US')()
console.log(thunk('A'))
console.log(thunk('B'))
console.log(thunk('C'))

thunk = thunkGetFromLocale('vi-VN')()
console.log(thunk('A'))
console.log(thunk('B'))
console.log(thunk('C'))

4 Comments

See also, "What is thunk?".
Hi, actually for the sake of example, i made the function getA, getB, getC similiar to each other, thats why in your solution it can all be combined to thunk(). But in reality, these 3 fn can be very different logic and return different things. Then its quite impossible to do your method right?
Instead of passing in 'A', 'B', or 'C', just pass the function and keep the locale paramater in each function definition. The thunk lazily knows the locale, it is just a wrapper...
@Mr.Polywhirl i see, then it's actually go against with my first goal is to make the code shorter but still consistent and easy to understand. Cause the thunk logic is quite long and hard to read though
0

You can easily create state outside of the function:

let locale = 'en-US';

function getA() {
 return 'A' + locale
}

But even though you are using functions now, I'm not sure if it can be called 'functional'.

If you modify state outside of the function, you pretty much have similar behavior as a class again, except now it's outside the class.

If you embrace functional programming, ideally the result of a function should not depend on side-effects.

So another way to handle this, is using a higher-order function:

function getAForLocale(locale) {
  return () => {
    return 'A' + locale;
  }
}

Now we can call:

const getA = getAForLocale('en-US');

And then we can re-use getA multiple times.

I think this is the most functional approach, but I can't say that this is better than using classes.

1 Comment

Yes, i think doing like this cannot be called functional, because it is not stateless and depend on outside dependency
-1

You can set a global default that you can pass as a default value for the locale in each function.

Update: If you want to change the defaultLocale; declare it as let, else keep it const.

let defaultLocale = 'enUS';

const getA = (locale = defaultLocale) => 'A' + locale;
const getB = (locale = defaultLocale) => 'B' + locale;
const getC = (locale = defaultLocale) => 'C' + locale;

let result = getA();

console.log(result);  // Output: AenUS

defaultLocale = 'fr'; // Change the default locale

console.log(getB());  // Output: Bfr


Update: If you want to go the thunk-route as suggested by hgb123, you can pass the function into the thunk.

const getA = (locale) => 'A' + locale;
const getB = (locale) => 'B' + locale;
const getC = (locale) => 'C' + locale;

const thunkFormatLocale = locale => () => func => func(locale);

let thunk = thunkFormatLocale('enUS')();
console.log(thunk(getA));
console.log(thunk(getB));
console.log(thunk(getC));

thunk = thunkFormatLocale('fr')();
console.log(thunk(getA));
console.log(thunk(getB));
console.log(thunk(getC));

2 Comments

Hi, thanks for your input. Actually i also need to provide a way to modify the defaultvalu( equivalent to the init method in class in the example). Do you have any idea on that ?
If you provide a way to update defaultValue, this is no longer functional.
-1

Expanding on @slebetman's answer, we can use modern Javascript features to make our closure more concise.

const initStringFormatter = locale => ({
  getA: () => 'A' + locale,
  getB: () => 'B' + locale,
  getC: () => 'C' + locale
});

let stringManager = initStringFormatter('enUS');

stringManager.getA();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.