Naming struct field ids in Racket when creating struct - struct

I am learning Racket at the moment and would like to know if the following is possible out of the box in Racket. When I create an instance of a class I use the following syntax:
(new Client% [name "John"]
[age 30])
I like the fact that I need to name the field ids when creating an instance of a class. Is there an equivalent when I am creating a struct. I scanned the Racket documentation but could not find anything about naming field ids when creating a struct.
TIA.

If you look at the documentation, you will find that struct creates a constructor for you that takes the initial values of a structure instance as positional arguments. AFAIK there is no built-in support for a "keyword"-like constructor.
Having said that, you can always do something like
(struct client (name age))
(struct client (name age) #:transparent)
(define (new-client #:name n #:age a)
(client n a))
(new-client #:age 20 #:name "me")
to get the usual function with keyword feeling.

Related

Replacing a static Enum with a HashMap or similar

I have a simple Enum used to map to hardcoded strings:
Think:
enum SomeThing {
A, B, C
}
These values are shown in a UI as RadioButtons so the user can pick one and these options are mapped to a string (the name of a file I don't control).
The way this is done at runtime time is (pseudo-code):
let xxx: HashMap::from([
(
SomeThing::A.to_string(),
"value_for_a".to_string(),
),
(
SomeThing::B.to_string(),
"value_for_b".to_string(),
),
etc...
This is done at runtime and later used to retrieve the value for the chosen option. The fact that it's an Enum has the type safety in mind. But...
I'd like to change this code to make it user-configurable, and allow the user to specify those values themselves; since it's a runtime-constructed HashMap, I could imagine a config file (of any format like YAML) having:
---
xxx:
A: value_for_a
B: value_for_b
C: value_for_c
And parsed into "the same" HashMap (without the benefits of the Enum type safety).
The questions are:
I suppose creating an Enum at runtime would not be possible right? I'd imagine the user could create a new D key/value and or remove an existing one.
What would be another way to do this instead? Keep/Use a HashMap of some sort to store the Key (A,B,C) and their values after they were parsed?
(worth mentioning: the Enum is convenient because it's used by structopt for a command line parameter via the provided arg_enum! macro, so you can specify the posible_values as &YourEnum::variants() but this is less important to keep)
Is there a "better" way to do this that you can think of?

Why can't I use C#9's "with" keyword to create a copy of structs (like with records)

C# 9 has a new feature. A record type. The record is just a class, but with a bunch of automatically created functions and properties. But basically the idea (as I undstand it) was, a class that behaves like structs, for things like copying, coimparison with Equals, immutibility and so on.
Also with the record type was a new feature with the keyword "with". To create a copy of a record, you can write something like that: var copy = original with { Property = new_value, };
Now I wondered, if records were designt to behave like structs (but are classes). Why doesn't the new "with" keyword works also with structs. I mean, as far as I can tell, structs have all features, that are necessary for this feature. Like they are copied by value.
Instead to use similar features for structs, I have to write a copy constructor and can then write: var copy = new StructType(original) { Property = new_value, };
Short answer:
That's how the feature was designed.
Long answer:
The compiler creates a synthesized clone method with a reserved name <Clone>$, when you use with keyword, the compiler calls this clone method, and then modifies whatever properties you want to modify.
structs or classes doesn't have a synthesized clone method. Hence, with can't be used with them.
You may want to write a language proposal to extend the usage of with keyword.
Edit:
Currently, there is a proposal for allowing record structs. See Proposal: record structs for more information. This is what you may want.

How to provide all functions associated with a struct in Racket

Is there a way to provide all of the functions associated with a struct without explicitly enumerating them?
For example, if I have foo.rkt:
#lang racket
(provide foo-struct) ; provide all functions, not just the constructor
(struct foo-struct (biz bop))
And then bar.rkt:
#lang racket
(require "foo.rkt")
(define foo (foo-struct 1 2)) ; works just fine
(foo-struct-biz foo) ; is undefined
Is there a way to provide foo-struct-biz and foo-struct-bop (and any other methods associated with the struct) without explicitly listing them?
Thanks to #PetSerAl in the comments, the way to do this is
(provide (struct-out foo-struct))
which will provide all of the methods associated with foo-struct.

Type families in Persistent library

When explaining the template expansion in the chapter on Persistent, the Yesod book has something looking like
newtype Key Person = PersonKey (BackendKey SqlBackend)
I'm not quite sure how to read this newtype declaration: is
PersonKey a something already defined somewhere else (couldn't
find its definition though) or is PersonKey just an ordinary
type or value constructor like Just/Nothing/Left/etc. ?
Alright, to simplify just a bit first, let's pretend it said:
newtype KeyPerson = PersonKey (BackendKey SqlBackend)
In this case, KeyPerson would be the "type constructor", and PersonKey would be the "data constructor." We would be creating both of them here. The PersonKey data constructor would hold a value of type BackendKey SqlBackend, which would need to exist elsewhere and be referenced from here.
In this case, we're usually associated types (aka type families), which makes things slightly more complicated: newtype Key Person means "we're defining an associated Key for the Person type. But we're still creating a PersonKey data constructor just as before.

Record syntax field type annotation having only smart constructor

I'm trying to create a record where one of the fields has a type that is not exported, since it's using a smart constructor. Using the smart constructor as the type does not work.
Not in scope: type variable `domain'
Maybe there is a language extension that allows me to do this, or something similar?
Exporting the constructor along with the smart constructor would allow me to solve this problem, but that in turn creates the possibility of creating values that the smart constructor wouldn't allow.
The (non-working) code I have right now:
import Domain (domain) -- Domain is not exported, and domain is a smart constructor for Domain
data Rec = Rec
{ dint :: domain Int -- what do I do here? I want it to be `Domain Int` but `Domain` isn't exported.
...
}
The issue here is confusion between the concept of a type constructor and a data constructor. For brevity, I will illustrate the difference with an example.
data Foo a = Bar [a]
In the expression above, Foo is the type constructor and Bar is the data constructor. The key difference is that Foo is a value in Haskell's type space and Bar is a value in its data space. A value in type space cannot be used in data space, and vice versa. For example, the compiler would error at the following expressions.
someVariable :: Bar Int
someVariable = Foo [15]
The next expression, however, is completely valid.
someVariable :: Foo Int
someVariable = Bar [15]
Additionally, all type constructors must start with an upper case letter. Any types starting with a lower case letter will be considered type variables, not type constructors (the a in our definition above is an example of this).
The introduction of smart constructors add another layer to this problem, but the key thing to understand is that smart constructors are data constructors, not type constructors. In your definition of Rec, you tried to use your smart constructor, domain, in the type declaration for dint field. However, because domain is a data constructor not a type constructor, and it is lower case, the Haskell compiler tried to interpret domain as the name of a type variable. Because you never specified a variable named domain in your definiton of the Rec type, the compiler raised an error.
You don't actually need to export the data constructor for Domain to solve the issue, just the type itself. This can be accomplished with the following.
module Domain (
Domain(), domain,
...
) where
Including Domain() in the export definition tells Haskell to export the Domain type constructor, but not any of its data constructors. This preserves the safety you wanted with the safe constructor, and allows you to define types correctly. You can now use your newly exported type in your definition of Rec.
import Domain (Domain(), domain)
data Rec = Rec
{ dint :: Domain Int
...
}
For more information, I strongly recommend you read the HaskellWiki articles on constructors and smart constructors.

Resources