I just started to learn functional programming world by using fp-ts lib. At this moment I can understand basic concept of proposed function by this lib, but I can't understand how to glue them all together in the single data flow.
I would like to share a user story that I want to implement and use it as an example for this question. It sounds like this:
- User should be able to book an appointment of selected specialist
I know it doesn't make sense to you at this moment, but let me show you how it looks in the code to be on the same page.
Note: This is pseudo code to make it more readable
const inputData = {
userId: 1,
specialistId: 2,
selectedServicesIds: ['a', 'b', 'c'],
startTime: 'today at 12:00'
}
const user = await fetchUserById(inputData.userId)
if (user === null) {
throw 'User not found'
}
const specialist = await fetchSpecialistById(inputData.specialistId)
if (user === null) {
throw 'Specialist not found'
}
const workingDay = await fetchWorkingDay(inputData.specialistId, inputData.startTime)
if (workingDay === null) {
throw 'WorkingDay not found'
}
const selectedServices = await fetchSelectedServices(inputData.specialistId, inputData.selectedServicesIds)
if (selectedServices.length < inputData.selectedServices) {
throw 'Some selected services are not belong to the selected specialist'
}
const selectedServicesDuration = calculateDuration(selectedServices)
const appointmentEndTime = addMinutes(inputData.startTime, selectedServicesDuration)
const existingAppointments = await fetchAppointmentsOfSpeciallist(inputData.specialistId)
const isAppointmentOverlapExistingAppointments = isOverlaps(existingAppointments, inputData.startTime, appointmentEndTime)
if (isAppointmentOverlapExistingAppointments) {
throw 'Appointment overlap existing appointments'
}
return new Appointment(inputData.userId, inputData.specialistId, ...)
As you can see this is typical imperative code:
- take input data
- fetch data from db
- apply validation
- return result
Now what I was able to achieve using fp-ts and Do
-notation
pipe(
RTE.Do,
RTE.apS('user', fetchUserById(args.input.clientId)),
RTE.apSW('specialist', fetchSpecialistById(args.input.specialistId)),
RTE.apSW('workingDay', fetchWorkingDay(args.input.specialistId, args.input.startDateTime)),
RTE.apSW('assignedServices', getAssignedServicesOfSpecialist(args.input.specialistId, args.input.servicesIds))
RTE.map({ user, specialist, workingDay, assignedServices } => {
// Do I need to write all logic here?
})
As you can see, there are few parallel requests to fetch related data, but don't know what to do next. If I just put the imperative logic from the previous example inside RTE.map
function it will look like I wrapped imperative code with some fp-ts functions.
Could you please give me an advice of how to split this into different function and how to glue them all together?