f# - How to set up non-trivial data persistence? -
most generally, question is: how set non-trivial data persistence in nice functional way?
prefer answers people real experience, people have done it. happy hear other thoughts on subject.
and now, let me clarify mean examples.
let's have data program handles , needs 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 matter how persistence functions implemented, let's go relational database, or document-based database, or file on disk. don't care right now.
and have program them:
module somelogic = let printemployees emps = let print { name = name, age = age } = printfn "%s %d" name age seq.iter print emps
so far straightforward.
now, let's have kind of data, department
, related employee
, yet needs stored independently (in separate table, separate collection, separate file).
why independently? honest, don't know. do know needs have own identity, can display/update without relation particular employee. this, infer storage must separate, i'm open alternative approaches.
so pretty same thing did employees:
module departments = type department = { name: string; /* etc. */ } module departmentspersistence = type departmentid = ... let getdepartment, updatedepartment, newdepartment = ...
but now, how express relationship?
attempt #1: ignore persistence.
in great time-honored tradition of "abstract things", let design our data model while pretending there no persistence. we'll add later. day. it's "orthogonal". it's "auxillary". or 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 every time load employee persistent storage, must load department well. wasteful. , then, employee
-department
1 relationship, in real application i'd have lot more that. what, load whole graph every time? prohibitively expensive.
attempt #2: embrace persistence.
ok, since can't directly include department
in employee
, include department's artificial identity, can load when needed.
module employees = type employee = { name: string; age: int; department: departmentid }
now model doesn't make sense on own: modeling storage, not domain.
then, turns out "logic" functions need department must parametrized on "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 functions also don't make sense on own. persistence has become intimately integral part of program, far being "orthogonal concept".
attempt #3: hide persistence.
ok, can't include department directly, , don't including id, do?
here's idea: can include 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 looks cleanest way (incidentally, orms do), still not without problems.
one, model doesn't exacly make sense on own: components included directly, others via "promise", without evident reason (in other words, persistence leaks through).
another, having department promise fine reading, how update? guess use lens instead of promise, gets more complicated.
so.
how people this? should compromise, works, doesn't? , have compromise, isn't there "pure" way?
since there real data-driven applications out there, there must way these things done, right? right?..
Comments
Post a Comment