#Seems like an antipattern, any intuition as to why?

1 messages · Page 1 of 1 (latest)

weary sun
#

So, this manner of reintroducing something like object-oriented programming seems obviously un-Gleamish to me:

import gleam/float

type PrecisionArithmetic {
  PrecisionArithmetic(
    add: fn(Float, Float) -> Float,
    sub: fn(Float, Float) -> Float,
    mul: fn(Float, Float) -> Float,
    div: fn(Float, Float) -> Float,
    change_precision: fn(Int) -> PrecisionArithmetic,
  )
}

fn with_precision(n: Int) -> PrecisionArithmetic {
  PrecisionArithmetic(
    add: rounded_op(fn(a, b) { a +. b }, n),
    sub: rounded_op(fn(a, b) { a -. b }, n),
    mul: rounded_op(fn(a, b) { a *. b }, n),
    div: rounded_op(fn(a, b) { a /. b }, n),
    change_precision: fn(d) { with_precision(n + d) },
  )
}

fn rounded_op(op: fn(Float, Float) -> Float, n: Int) {
  fn(lt, rt) { op(lt, rt) |> float.to_precision(n) }
}

fn show(v) {
  echo v
  v
}

pub fn main() {
  let ops = with_precision(3)
     ops.add(123.456, 100.1) |> show // -> 223.556
  |> ops.mul(0.1)            |> show // -> 22.356
  let ops = ops.change_precision(-2)
     ops.sub(123.456, 0.0001) |> show // -> 123.5
  |> ops.div(10.0)            |> show // -> 12.4
}

This code is obviously a bit silly (maybe itself a warning...) and I mean it just as an example of the pattern--but can anyone put words to the intuition here? What is it about parameterizing a set of functions, possibly with some closed-over state accessible only to those functions, that we want to avoid?

In this example it is a bit glaring how the change_precision function clashes with the pipe, and of course part of the point of the pipe operator is to allow us to emulate the object-oriented style but with looser coupling between functions. But I've also found myself writing modules where every function takes a custom type as the first parameter, possibly with additional customizing parameters common between functions, which starts to feel a bit silly in its own way.

Thoughts?

static bramble
#

any more concrete reason why passing the custom type as the first function parameter feels silly? it's either that or "real" OOP (:

but as you rightfully said on your own, mimicking oop in gleam is a bit silly.

you might like this article, I have a feeling this is going into the direction your brain is leading you towards: https://mckayla.blog/posts/all-you-need-is-data-and-functions.html

mortal python
#

It’s not silly, it’s very normal

#

This is also how OOP works

#

In pretty much all styles of programming functions will typically take some important data as an argument

#

With your example you could reduce boilerplate, remove possibility of misuse, and reduce memory usage by storing the precision in the custom type instead of multiple closures

#

Also, understand that blog post is not advice on how to write Gleam, the pattern is shows is not normal or encouraged. It is instead a blog about the expressive power of type classes that uses Gleam code to express the ideas

static bramble
weary sun
#

I suppose what I'm thinking is that when I write a module with a custom type and a set of functions with that type as a first parameter, essentially what I've written is a class, except it isn't a first-class object in the language semantics and the syntax for working with it is more verbose, plus I can't easily manipulate the context the functions are executing in. So it seems like I've lost some ease-of-use power.

Perhaps the appropriate rejoinder is, Gleam favoring immutability, there isn't (or shouldn't be..?) a sense in which that context can be manipulated; if you want to change the values in scope to a function, pass different parameters.

Thanks for that link, I've also been thinking about traits/the lack of them and how to build extensible types without them; that was a useful read. Though I can see why the specific patterns in the post would be discouraged.

Maybe the upcoming game jam will be enlightening. I think of game dev as something like OOP's brightest hour, and FP's darkest, so hopefully there will be some good code to read that demonstrates alternatives to the architectual patterns I'm used to. (Though hoping for good code from a jam may be a bit much to ask lol)

mortal python
#

It’s not a class because a class has a shared vtable of functions

#

And it can’t be broken in the same ways this design can be

#

I think it’s a bit odd to say that game dev is great for OOP and bad for FP as both work fine, but DOD is generally considered much better in that space

#

The FP problem is mostly that there’s just not many existing libraries and engines for it. Carmack did a great talk about immutable FP in game dev and how it would be a great fit