Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
Most generally, the question is: how do you set up non-trivial data persistence in a nice functional way?
I would much prefer answers from people with real experience, people who have actually done it. But I would also be happy to hear any other thoughts on the subject.
And now, let me clarify what I mean with some examples.
Let's say I have some data that my program handles and needs to keep. Say, our old friend Employee:
module Employees =
type Employee = { Name: string; Age: int; /* etc. */ }
module EmployeesPersistence =
type EmployeeId = ...
let getEmployee: (id:EmployeeId -> Employee) = ...
let updateEmployee: (id:EmployeeId -> c:Employee -> unit) = ...
let newEmployee: (c:Employee -> EmployeeId) = ...
Doesn't really matter how persistence functions are implemented, let's say they go to a relational database, or to a document-based database, or even to a file on disk. I don't care right now.
And then I have a program that does something with them:
module SomeLogic =
let printEmployees emps =
let print { Name = name, Age = age } = printfn "%s %d" name age
Seq.iter print emps
So far so straightforward.
Now, let's say I have another kind of data, Department, which is related to Employee, yet needs to be stored independently (in a separate table, separate collection, separate file).
Why independently? To be honest, I don't really know. What I do know is that it needs to have its own identity, so that I can display/update it without relation to any particular employee. From this, I infer that the storage must be separate, but I'm open to alternative approaches.
So I could do pretty much the same thing as I did with Employees:
module Departments =
type Department = { Name: string; /* etc. */ }
module DepartmentsPersistence =
type DepartmentId = ...
let getDepartment, updateDepartment, newDepartment = ...
But now, how do I express the relationship?
Attempt #1: ignore persistence.
In the great time-honored tradition of "abstract all the things", let us design our data model while pretending that there is no persistence. We'll add it later. Some day. It's "orthogonal". It's "auxillary". Or some such.
module Employees =
type Employee = { Name: string; Age: int; Department: Department }
module SomeLogic =
let printEmployees emps =
let print { Name = name, Age = age, Department = { Name = dept } } = printfn "%s %d (%s)" name age dept
Seq.iter print emps
This approach means that every time I load an employee from persistent storage, I must load department as well. Wasteful. And then, Employee-Department is just one relationship, but in a real application I'd have a lot more than that. So what, do I load the whole graph every time? Prohibitively expensive.
Attempt #2: embrace persistence.
Ok, since I can't directly include Department in Employee, I will include department's artificial identity, so I can load it when needed.
module Employees =
type Employee = { Name: string; Age: int; Department: DepartmentId }
Now my model doesn't make any sense on its own: I am now essentially modeling my storage, not my domain.
Then, it turns out that "logic" functions that need department must be parametrized over the "loadDepartment" function:
module SomeLogic =
let printEmployees loadDept emps =
let print { Name = name, Age = age, Department = deptId } =
let { Name = deptName } = loadDept deptId
printfn "%s %d (%s)" name age deptName
Seq.iter print emps
So my functions now also don't make sense on their own. Persistence has become an intimately integral part of my program, far from being an "orthogonal concept".
Attempt #3: hide persistence.
Ok, so I can't include department directly, and I also don't like including its Id, what do I do?
Here's an idea: I can include a promise of department.
module Employees =
type Employee = { Name: string; Age: int; Department: () -> Department }
module SomeLogic =
let printEmployees emps =
let print { Name = name, Age = age, Department = dept } = printfn "%s %d (%s)" name age (dept()).Name
Seq.iter print emps
So far this looks like the cleanest way (incidentally, this is what ORMs do), but still not without problems.
For one, the model doesn't exacly make sense on its own: some components are included directly, others via "promise", without any immediately evident reason (in other words, persistence leaks through).
For another, having department as a promise is fine for reading, but how do I update? I guess I could use lens instead of promise, but then it gets even more complicated.
So.
How do people actually do this? Where should I compromise, what works, what doesn't? And do I really have to compromise, isn't there a "pure" way?
Since there are obviously real data-driven applications out there, there must be some way these things are done, right? Right?..
Related
I often use anonmymous records to build on the go data that will eventually be serialized and returned to be consumed somewhere else. This is very flexible, quick to deal with, etc.
But one thing I'm really missing is the ability to do something like this pseudo code:
{|
SomeData = 3
StartTime = ....
if ProcessedFinished then yield { EndTime = ... }
|}
when I could add a field, or not, based on some conditions.
Is there a solution to have the flexibility of the anonymous record, yet have the ability to make conditions like this? I could do a dictionary, then insert values based on conditions but then I'd have to box all the values as well and it would be messy right away.
What you're after is not possible today, but you can get pretty close like so:
let initialData =
{|
SomeData = 3
StartTime = ....
|}
if ProcessedFinished then
{| initialData with EndTime = ... |}
else
initialData
Of course, to represent this as a return type it's now more involved, as you'll likely need to use SRTP in the signature to account for any type with a SomeData and StartTime member on it. It's perfectly fine if this is just in the body of a routine that then produces another result to return, though.
If you want to represent data that may be optionally available based on a condition, especially if you want it to live outside of a given function, I'd recommend an option for that field instead.
I'm starting with F# and I read about recursive Union Types on https://fsharpforfunandprofit.com/posts/fsharp-in-60-seconds//
Given the example from the link
type Employee =
| Worker of Person
| Manager of Employee list
How can I create a value of type Manager and assign a List of Workers to it?
I can create a value of type Worker like this:
let john = {First = "John"; Last="Doe"}
let worker = Worker john
But I don't know how to create a value jane of type Manager having john assigned as an item of the Empoyee list:
let jane = { First="Jane"; Last="Doe" }
let manager = Manager [worker]
This works, but I can't assign First and Last from jane to manager.
Is manager meant to have no First / Last at all in this example?
How would I have to change the type definition for Manager to have both a list of Employee?
In addition it also looks like Manager doesn't have a member employees I can access after creating a value of type Manager?
Given that type definition, a manager cannot have first/last name. The type definition does not include anything like that.
To make Manager work similarly to Worker in this regard, just give it a Person value:
type Employee = Worker of Person | Manager of Person * Employee list
Then you can create a manager Jane:
let manager = Manager ({ First = "Jane"; Last = "Doe" }, [worker])
As for a member employees, there are several things to say.
First, stay away from members. F# is functional-first. Functions are better than members (for reasons that I'm not going to go into here). Have a function:
let employees emp =
match emp with
| Manager (_, emps) -> emps
| Worker _ -> ??? // what to return if the employee turned out to be worker?
And right away you can see a difficulty: when the employee is a Manager, sure, we can return their employees. But what if it's a Worker? What to return then?
One reasonable suggestion would be to return an empty list, because, after all, a Worker doesn't have any employees working for them, right?
let employees emp =
match emp with
| Manager (_, emps) -> emps
| Worker _ -> []
Another possibility would be to return an Option. Which approach is "correct" depends on what you're going to do with the result, so only you can answer that.
But if you absolutely insist on having a member (and please, please carefully think about why you need it), you can definitely add a member to your type like this:
type Employee = Worker of Person | Manager of Person * Employee list
with
member this.employees =
match this with
| Manager (_, emps) -> emps
| Worker _ -> []
I've been making a game with the LOVE2D game engine, and I've stumbled across an issue. I want to access a variable inside a nested table, but I don't know how.
Here's my code right now:
local roomNum = 1
local rooms = { r1 = { complete = false, name = "Room 1" }
if rooms[roomNum].complete == true then --problematic line
--do stuff
end
If I replace rooms[roomNum].complete with rooms.r1.complete then it works.
Any help would be appreciated!
'http://lua-users.org/wiki/TablesTutorial'
The provided link gives easy to understand examples on tables in Lua, so it may prove a useful resource in the future.
As for the why the replacement code worked, a dictionary is just sets of key/value pairs (kvp) . In examples from other languages, these pairs are normally shown as something like KeyValuePair.
In your case, you are using a variation on how dictionaries are used. As you have seen, you can use numbered indexes like room[1], or you can use a string like room["kitchen"]. It gets interesting when you provide a set of data to initialize the dictionary.
Building off of the provided data, you have the following:
local rooms = { r1 = { complete = false, name = "Room 1" }
r1 is equivalent to using rooms["r1"] without the dataset. In providing the dataset, any "named" Key can be referenced like it is a property of the dictionary (think of classes with public getter/setter). For the named keys of a dataset, you can provide a key as numbers as well.
local rooms = { [1] = { complete = false, name = "Room 1" }
This indexing fits the direction you were headed on providing a room index. So, you could either swap the dataset to use integers instead of r1, r2 and so on, or you could concatenate r and the index numbering. That is pretty much up to you. Keep in mind as you go further down nesting the same rules apply. So, complete could look like rooms[1].complete, rooms["r1" ].complete, or rooms.r1.complete.
I'm working on some sort of advanced search feature with variable fields. Some of the search fields are lists of some primitive(ish) objects (String, enums, etc.). I want to be able to retrieve records whose values are a subset of some given list.
To illustrate, say I have a Book class (assume the model is appropriate this way):
class Book {
...
List authors = []
...
}
In addition say we have the following book records:
Book(title: 'Great Expectations of Tom Sawyer', authors: ['cdickens', 'mtwain'])
Book(title: 'Huckleberry Potter in Bleak House', authors: ['cdickens', 'mtwain', 'jrowling'])
Book(title: 'A Christmas Carol', authors: ['cdickens'])
Then, I'm given a list of author (names) authorFilter = ['cdickens', 'mtwain'] to search for any collaborative works of cdickens and mtwain. How do I express this using GORM's where construct? Is it even possible to cover this using it?
Basically, what I want to do is:
Book.where {
authorFilter.every { it in authors }
}
This question has come up before. Unfortunately, where nor criteria queries have an every() equivalent. But there's a hack that may work for you. But first, I'll expand on your domain model.
Domain model
class Book {
String title
static hasMany = [authors: Author]
static belongsTo = Author
}
class Author {
String name
static hasMany = [books: Book]
}
HQL query
Using the domain model described above, you can use the following HQL query.
def hql = """
SELECT b FROM Book AS b
INNER JOIN b.authors AS a
WHERE a.name in :authors
GROUP BY b
HAVING COUNT(b) = :count
"""
def books = Book.executeQuery(hql, [authors: authorFilter, count: authorFilter.size()])
How it works.
You can read about how this query works in the other question I mentioned.
I don't think this is any better than #EmmanuelRosa's answer but I have another approach using HQL and the executeQuery method.
Using the same domain model he's given in his answer, I use the MEMBER OF expression to restrict the results.
def authorFilter = [Author.get(1)]
def authorMemberOfRestriction = ""
def namedParameters = [:]
authorFilter.eachWithIndex{ aut, ind ->
authorMemberOfRestriction += ":author${ind} MEMBER OF b.authors AND "
namedParameters.put("author" + ind, aut)
}
namedParameters.put('count', authorFilter.size())
def hql = """
FROM Book b
WHERE
(
${authorMemberOfRestriction}
size(b.authors) = :count
)
"""
def books = Book.executeQuery(hql, namedParameters)
Mine is somewhat different in that the authorFilter is a collection of Author domain class instances; I found it to work much easier for the MEMBER OF expression and truthfully is more of a depiction of how the real data would be modeled.
You can see that I build the multiple MEMBER OF expressions with the eachWithIndex, using the index on both sides of the named parameters. It's not exactly pretty but I don't believe there is a way around this and still use this approach.
I think #EmmanuelRosa's approach is probably the 'cleaner' option but the MEMBER OF approach makes more sense in my head as far as the logic is concerned.
There doesn't seem to be a simpler way of doing this other than doing HQL query. Taking hints from this answer to a very similar question, I figured out a solution to my problem.
To be clear, the Book should already have relation to many String using the hasMany construct:
class Book {
...
static hasMany = [authors: String]
...
}
To fetch results:
def results = Product.executeQuery("select p from Products p join p.tags t where t in :tags", [tags: givenTags])
In requests to my application i often get ids of the objects, which should be associated. However I have to perform a check to see if they are.
Example scenario:
Class A and B are associated:
A {
static hasMany = [bs: B]
}
In my request I will get aid and bid.
What I usually do is:
def a = A.get(aid)
def b = a.bs.find {it.id == bid}
What would be a better way to make this check? From performance point of view?
Thanks
If B has a belongsTo = [ a : A ] defined in it, then you can do this:
def a = A.get(aid)
def b = B.find("from B as b where b.id=:id and b.a = :a", [id:bid, a:a])
if (b) {
// b exists -- do something with it here
} else {
// uh oh! b isn't within a
}
This won't do an iteration over all the sets elements like your code. Essentially, it's the same as erturne's solution, but this actually loads the object.
My first inclination is to drop to HQL and use count() to see if it exists. There may be more elegant ways to achieve the same thing (perhaps using withCriteria) but my first crack at it looks like:
def result = A.executeQuery("select count(*) from A as a inner join a.bs as b where a.id=:aid and b.id=:bid", [aid: aid, bid: bid] )
def associated = result[0] > 0
I think that would be pretty efficient, although anytime you're looking at improving performance it's best to take measurements of different implementations so you can compare them for yourself. I'll leave that as an exercise for the reader. ;-)
EDIT: I think it's efficient because I've left the heavy lifting in the database rather than pulling data for instances of A and B across the network, creating instances in memory, or iterating over results.