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

Using prototype in records #1316

Open
6 tasks
xp44mm opened this issue Sep 13, 2023 · 14 comments
Open
6 tasks

Using prototype in records #1316

xp44mm opened this issue Sep 13, 2023 · 14 comments

Comments

@xp44mm
Copy link

xp44mm commented Sep 13, 2023

I propose we Use prototype in records

type Production = string list

type ProductionsCrew = 
    {mainProductions:Set<Production>; augmentedProductions:Set<Production>}

type NullableCrew =
    {prototype:ProductionsCrew;symbols:Set<string>;nonterminals:Set<string>;terminals:Set<string>;nullables:Set<string>}

type GrammarCrew = 
    {prototype:NullableCrew;firsts:Map<string,Set<string>>;lasts:Map<string,Set<string>>;follows:Map<string,Set<string>>;precedes:Map<string,Set<string>>}

A prototype chain is a type inheritance, Referencing fields in prototype records can omit prototype

let g: GrammarCrew =..
let prods = g.prototype.prototype.augmentedProductions
let prods = g.augmentedProductions

All function parameters of the base class can be applied to the data of the derived class.

let letftmost (nullable:NullableCrew ) = ()

let n: NullableCrew = ..
leftmost n //ok

let g: GrammarCrew =..
leftmost g //ok

The existing way of approaching this problem in F# is ...

type Production = string list

type ProductionsCrew(mainProductions:Set<Production>, augmentedProductions:Set<Production>) =
    member _.mainProductions = mainProductions
    member _.augmentedProductions = augmentedProductions

type NullableCrew(crew:ProductionsCrew,symbols:Set<string>,nonterminals:Set<string>,terminals:Set<string>,nullables:Set<string>) =
    inherit ProductionsCrew(crew.mainProductions,crew.augmentedProductions)
    member _.symbols = symbols
    member _.nonterminals = nonterminals
    member _.terminals = terminals
    member _.nullables = nullables

type GrammarCrew(crew:NullableCrew,firsts:Map<string,Set<string>>,lasts:Map<string,Set<string>>,follows:Map<string,Set<string>>,precedes:Map<string,Set<string>>) =
    inherit NullableCrew(ProductionsCrew(crew.mainProductions,crew.augmentedProductions),crew.symbols,crew.nonterminals,crew.terminals,crew.nullables)
    member _.firsts = firsts
    member _.lasts = lasts
    member _.follows = follows
    member _.precedes = precedes

Pros and Cons

The advantages of making this adjustment to F# are ...

  • Simpler code

  • Reduced one base class construction when initializing derived classes

The disadvantages of making this adjustment to F# are ...

  • The prototype class must be reference transparent, with the same input parameters, and the class must be equivalent.

  • I don't know how to solve the problem of duplicate names on the prototype chain yet. I suggest not allowing duplicate names for now.

Extra information

Estimated cost (XS, S, M, L, XL, XXL):

Related suggestions: (put links to related suggestions here)

Affidavit (please submit!)

Please tick these items by placing a cross in the box:

  • This is not a question (e.g. like one you might ask on StackOverflow) and I have searched StackOverflow for discussions of this issue
  • This is a language change and not purely a tooling change (e.g. compiler bug, editor support, warning/error messages, new warning, non-breaking optimisation) belonging to the compiler and tooling repository
  • This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it
  • I have searched both open and closed suggestions on this site and believe this is not a duplicate

Please tick all that apply:

  • This is not a breaking change to the F# language design
  • I or my company would be willing to help implement and/or test this

For Readers

If you would like to see this issue implemented, please click the 👍 emoji on this issue. These counts are used to generally order the suggestions by engagement.

@xp44mm xp44mm changed the title Using prototyp in records Using prototype in records Sep 13, 2023
@xp44mm
Copy link
Author

xp44mm commented Sep 13, 2023

Also to this suggestion, Derived classes inherit from input arguments:

type Production = string list

type ProductionsCrew(mainProductions:Set<Production>, augmentedProductions:Set<Production>) =
    member _.mainProductions = mainProductions
    member _.augmentedProductions = augmentedProductions

type NullableCrew(crew:ProductionsCrew,symbols:Set<string>,nonterminals:Set<string>,terminals:Set<string>,nullables:Set<string>) =
    inherit &crew //*
    member _.symbols = symbols
    member _.nonterminals = nonterminals
    member _.terminals = terminals
    member _.nullables = nullables

type GrammarCrew(crew:NullableCrew,firsts:Map<string,Set<string>>,lasts:Map<string,Set<string>>,follows:Map<string,Set<string>>,precedes:Map<string,Set<string>>) =
    inherit &crew //*
    member _.firsts = firsts
    member _.lasts = lasts
    member _.follows = follows
    member _.precedes = precedes

@LyndonGingerich
Copy link

I'm confused. Your title says that this suggestion is for records, but your "existing way of approaching this problem" section uses classes. Which do you mean?

Should this:

type ProductionsCrew(mainProductions:Set<Production>, augmentedProductions:Set<Production>) =
    member _.mainProductions = mainProductions
    member _.augmentedProductions = augmentedProductions

instead be written as this?

type ProductionsCrew =
    { mainProductions: Set<Production>
      augmentedProductions: Set<Production> }

@xp44mm
Copy link
Author

xp44mm commented Oct 17, 2023

Prototypes are similar to inheritance, currently only classes can inherit in fsharp. Therefore, the current method is to use classes instead of records to implement prototypes.

@LyndonGingerich
Copy link

Record inheritance has already been suggested and rejected in #70. A syntax change would not overrule Don Syme's reasons against it.

@LyndonGingerich
Copy link

Do you have a specific use case you could describe? We may be able to find a better solution.

@realparadyne
Copy link

Record inheritance has already been suggested and rejected in #70. A syntax change would not overrule Don Syme's reasons against it.

I just went and looked at that and thought, cool - syntactic sugaring by the compiler to insert the 'inherited' fields to save typing them out again and then having 2 copies to maintain later. I didn't think of it involving the CLR type system at all, it would be erased by the compiler into a new flat record. Maybe if it were described that way the objection would change?

@LyndonGingerich
Copy link

Derived classes inherit from input arguments:

In general, make separate suggestions in separate issues.

I would not recommend suggesting this, though. If the inheriting class takes two instances of the inherited class as parameters, which is inherited? I couldn't guess by looking at the code. If you already have an instance of the inherited object, it sounds to me as though you might have a "has-a" relationship rather than an "is-a" relationship, so you might consider using a property rather than inheriting.

@LyndonGingerich
Copy link

@realparadyne I don't know, but that almost sounds more similar in intention to extracting a record with the common fields to use in both. Or might the proposed spread operator (#1253) help?

@realparadyne
Copy link

@LyndonGingerich I thought of spread but that lets you make a new record instance at a point when you have instances of the other records you want to incorporate. It doesn't make a new, named type before you create instances of it. I'm not saying I have any use cases for this feature, but then again it's never been an option.

@LyndonGingerich
Copy link

It doesn't make a new, named type before you create instances of it.

It might. Don Syme suggested the use of the spread operator in type definitions in the linked issue, though he says he's "a bit ambivalent" on that.

@xp44mm
Copy link
Author

xp44mm commented Oct 18, 2023

Records with prototypes can indeed be separated from normal records. Maybe He should be marked with [<NoEquality; NoComparison>]. I don't know what he is, just call him prototype record.

The prototype and the prototype of JavaScript are the same, so the use cases of the prototype can refer to the prototype of JavaScript.

One of the use cases is to have the function return all immutable calculation results. When you first calculate the return and many results, then another calculate has other results based on the results of the first step, and so on, the members of these results do not need to be nested, resulting in too many long name references. a.b.c.e.f

The definition of a type can concisely indicate the calculation process and dependency relationships.

This is the desired effect:

https://github.com/xp44mm/FslexFsyacc/blob/master/FslexFsyacc.Prototypes/ItemCoreCrews.fs

This is the equivalent type definition by code generation

https://github.com/xp44mm/FslexFsyacc/blob/master/FslexFsyacc/Yacc/ItemCoreCrews.fs

These two are initialization codes:

https://github.com/xp44mm/FslexFsyacc/blob/master/FslexFsyacc/Yacc/ProductionCrewUtils.fs

https://github.com/xp44mm/FslexFsyacc/blob/master/FslexFsyacc/Yacc/ItemCoreCrewUtils.fs

@LyndonGingerich
Copy link

LyndonGingerich commented Oct 18, 2023

Records with prototypes can indeed be separated from normal records.

I'm confused. What are you responding to here?

Unfortunately, I'm not familiar with JavaScript.

If I understand correctly, you are creating records using helper functions, but you are frustrated that certain fields are nested too deeply. Do I understand correctly?

Just to be sure, have you first tried moving fields to parent records to avoid nesting?

I didn't think of it involving the CLR type system at all, it would be erased by the compiler into a new flat record. Maybe if it were described that way the objection would change?

@realparadyne I honestly don't know. If you think your suggestion has not yet been addressed and would be useful, maybe you should open an issue and suggest it.

@xp44mm
Copy link
Author

xp44mm commented Oct 19, 2023

If I understand correctly, you are creating records using helper functions, but you are frustrated that certain fields are nested too deeply. Do I understand correctly?

correctly, certain fields are nested too deeply. I cannot provide the name of the intermediate path because as long as the target field name is sufficient to locate it.

Just to be sure, have you first tried moving fields to parent records to avoid nesting?

Yes, but apart from No moving, the data is still in place, i hope fsharp can omit the intermediate special prototype path and obtain the target field.

suggest:

let prods = g.augmentedProductions

instead of:

let prods = g.prototype.prototype.augmentedProductions // any number of prototype

@xp44mm
Copy link
Author

xp44mm commented Oct 19, 2023

Records with prototypes can indeed be separated from normal records.

Keep the traditional recording system unchanged and add a new data type that can be prototype.

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

No branches or pull requests

3 participants