F# — Самый чистый способ извлечь/Развернуть типизированное значение ожидаемого регистра из Дискриминационного объединения?

#f# #discriminated-union

#f# #дискриминируемый-союз

Вопрос:

Общая структура типов и использование в моем текущем F# работают очень хорошо. Тем не менее, я хочу получить некоторую перспективу, если я делаю что-то неправильно или следую какому-то анти-шаблону. Я действительно очень часто обнаруживаю, что, по сути, ожидаю определенного типа в определенной логике, который извлекается из более общего типа, представляющего собой дискриминированное объединение, объединяющее множество различных типов, которые все следуют уровням общей обработки.

По сути, мне нужны конкретные версии этой функции:

 'GeneralDiscriminatedUnionType -gt; 'SpecificCaseType  

Я ловлю себя на том, что повторяю множество утверждений, подобных следующему:

 let checkPromptUpdated (PromptUpdated prompt) = prompt  

Это самый простой способ, который я нашел для этого; однако у каждого из них есть действительное предупреждение компилятора, которое имеет смысл, что может возникнуть проблема, если функция вызывается с типом, отличным от ожидаемого. Это справедливо, но у меня пока их от 40 до 50 штук.

Поэтому я начал пробовать следующее, что на самом деле лучше, потому что это вызвало бы допустимое исключение с неправильным использованием (оба они одинаковы).:

 let checkPromptUpdated input = match input with | PromptUpdated prompt -gt; prompt | _ -gt; invalidOp "Expecting Prompt"  let checkPromptUpdated = function | PromptUpdated prompt -gt; prompt | _ -gt; invalidOp "Expecting Prompt"  

Тем не менее, это выглядит намного запутаннее, и я пытаюсь выяснить, есть ли у кого-нибудь какие-либо предложения, прежде чем я начну все это безобразие.

Есть ли какой-то способ применить эту более широкую логику к более общей функции, которая затем позволила бы мне написать это в 50-100 раз более чистым, прямым и читаемым способом?

Этот вопрос-всего лишь вопрос попытки написать более чистый код.

Это пример DU, для которого я пытаюсь написать функции, чтобы иметь возможность извлекать определенные типизированные значения из обращений:

 type StateEvent =  | PromptUpdated of Prompt | CorrectAnswerUpdated of CorrectAnswer | DifficultyUpdated of Difficulty | TagsUpdated of Tag list | NotesUpdated of Notes | AuthorUpdated of Author  

Ответ №1:

Если checkPromptUpdated функция работает только с событиями, которые имеют отношение к PromptUpdated делу, то я думаю, что лучший дизайн заключается в том, что функция должна принимать только значение типа Prompt (вместо значения типа StateEvent ) в качестве аргумента:

 let checkPromptUpdated prompt =   // do whatever checks you need using 'prompt'  

Конечно, это означает, что сопоставление шаблонов будет перенесено из этой функции в функцию, которая ее вызывает, или далее — в место, где вы действительно получаете StateEvent и должны обрабатывать все остальные случаи. Но это именно то, что вам нужно — как только вы сопоставите шаблон события, вы сможете работать с более конкретными типами, такими как Prompt .

Комментарии:

1. Спасибо, Томас. Это направление имеет смысл, и я собираюсь попытаться ориентироваться на более широкую логику, чтобы соответствовать этому. Это действительно выгодно благодаря возможности обеспечить исчерпывающее сопоставление шаблонов в событии состояния. Проблема в нашей конкретной области-дизайне… Мы смогли отделить и ограничить область применения различных аспектов предметной области/бизнес-логики, чтобы они были автономными, в то время как существует более широкая «техническая сантехника», которая абстрагируется. Это было хорошо, но привело к тому, что мы стали вызывать определенные функции для передачи определенных типов в общие функции сантехники.

2. Я не уверен, имеет ли смысл мой предыдущий комментарий, но, короче говоря… чтобы уменьшить количество повторяющегося кода «сантехники», у нас есть очень простые модули доменной логики (читаемые заинтересованными сторонами, не являющимися разработчиками), которые были сведены к очень простым вещам. Это работает довольно элегантно, но, по сути, нам нужно пройти эти проверки, чтобы заставить его работать. Однако то, что вы предложили, заставляет меня задуматься о том, чтобы найти способы переориентировать » где » на самом деле должна быть написана эта конкретизация. Есть о чем подумать….! Однако я рассмотрю эту переориентацию позже. Спасибо.