Skip to content
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

Feature.lambdas #34

Merged
merged 25 commits into from
Jul 12, 2022
Merged

Feature.lambdas #34

merged 25 commits into from
Jul 12, 2022

Conversation

jneen
Copy link
Contributor

@jneen jneen commented Jun 16, 2022

Lambdas and accessor syntax!

Adds lambda syntax as follows:

?x => x + 1
?x ?y => x + y

as well as three functions that use them:

each(list, ?element => ...) // equivalent to ruby .map
filter(list, ?element => true or false) // equivalent to ruby .filter or .select
roll(init, list, ?accum ?element => new_accum) // equivalent to inject or fold

This also introduces accessor syntax for structs and certain declared types:

{ a: 1 }.a // 1
filter(field:event_times, ?et => et.type = 'setup')

In order to register a declared type as having struct accessors, we must use the structable declaration in the declared type:

  Dentaku::Type.declare(:event_type) do
    structable(
      type: ':string',
      start_time: ':numeric',
      end_time: ':numeric',
    )
  end

@jneen jneen requested review from hale and joshuabates June 16, 2022 15:15
@hale
Copy link
Contributor

hale commented Jun 16, 2022

Nice, @jneen!

As an exercise, I'd like to see what this would look like for a real example. I think it will be instructive for anything else we need and to show how this will work once it's handed to config.

For example, in https://github.com/opencounter/opencounter/issues/3202 Jeff describes a fee structure that is based on the number of hours the event lasts per day. The fee is charged as follows: each hour the event takes place costs $175, with a minimum 4h charge per day. In other words, there is a minimum charge of $700 per day for the first four hours, and then any hour above that is an additional $175 per hour.

Would a function that calculates that fee look something like this?

sum(map(hours_per_day(field:event_times), ?cost, ?hours => ceiling(700, 175 * hours))

(Assumes a function hours_per_day that groups the event periods per day and sums their hours, since periods can span multiple days....not sure if that's the way to go but just an example.)

  1. What is the type of the accumulator for roll, is it always an integer? What is it's initial value?

Copy link
Collaborator

@joshuabates joshuabates left a comment

Choose a reason for hiding this comment

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

👍

@@ -28,30 +28,18 @@ class Addition < Arithmetic
def operator
:+
end

def self.precedence
Copy link
Collaborator

Choose a reason for hiding this comment

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

did we stop needing this after the parser rewrite?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep! Precedence is now hardcoded in the parser, which is honestly where it should live.

require_relative 'case/case_when'
require_relative 'case/case_then'
require_relative 'case/case_switch_variable'
require_relative 'case/case_else'
Copy link
Collaborator

Choose a reason for hiding this comment

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

How are we able to delete all these case child nodes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Leftover from the parser

@jneen
Copy link
Contributor Author

jneen commented Jun 17, 2022

Nice, @jneen!

As an exercise, I'd like to see what this would look like for a real example. I think it will be instructive for anything else we need and to show how this will work once it's handed to config.

For example, in opencounter/opencounter#3202 Jeff describes a fee structure that is based on the number of hours the event lasts per day. The fee is charged as follows: each hour the event takes place costs $175, with a minimum 4h charge per day. In other words, there is a minimum charge of $700 per day for the first four hours, and then any hour above that is an additional $175 per hour.

Would a function that calculates that fee look something like this?

sum(map(hours_per_day(field:event_times), ?cost, ?hours => ceiling(700, 175 * hours))

Something similar to this!

sum(each(seconds_by_day_for_type(field:event_times, 'event'), ?seconds => 175 * clamp_below(4, floor(seconds/(60*60)))))

(Assumes a function hours_per_day that groups the event periods per day and sums their hours, since periods can span multiple days....not sure if that's the way to go but just an example.)

This could now be theoretically written in Dentaku as well.

2. What is the type of the accumulator for `roll`, is it always an integer? What is it's initial value?

Fixed the example, the initial value should be passed in. The type can be any type, just must be the same as the initial value and the result.

@jneen
Copy link
Contributor Author

jneen commented Jun 17, 2022

If I'm honest, I want to start by introducing filter, since it's imo the most intuitive use of lambda. I want to hold off on explaining roll until we have an actual use case - everything I've seen so far should be able to be handled by filter and each, with the occasionall sum (which is secretly a fold, but much simpler to understand.)

@jneen jneen merged commit 546a93d into master Jul 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants