ReasonML:如何获取多态变体标签为变量并泛化代码?
在ReasonML中处理多态变体标签与代码泛化
我来帮你拆解这两个问题:
1. 能不能把多态变体的标签作为变量获取?
在ReasonML里,多态变体的标签本身没法直接当成变量用,但我们可以用两种方式间接实现类似需求:
直接引用构造函数:你可以把变体的构造函数(也就是那个
`Todo标签)当作值来赋值和传递,比如:type todo = {id: int, text: string}; type idFunction = todo => int; type recordType = [ | `Todo(todo, idFunction) | `TodoItem(todoItem, idFunction) ]; // 把`Todo`构造函数存成变量 let todoConstructor = `Todo; // 用这个变量构造变体实例 let myTodo = todoConstructor({id: 1, text: "Buy milk"}, todo => todo.id);提取标签的字符串标识:如果需要获取标签的名称(比如用于日志或动态判断),可以通过模式匹配手动映射:
let getTagName = (record) => { switch(record) { | `Todo(_, _) => "Todo" | `TodoItem(_, _) => "TodoItem" }; };这种方式虽然需要手动维护映射,但对于变体标签不多的场景来说足够实用。
2. 泛化你的commitItemToSchema函数
你的原始代码里,两个分支的逻辑几乎一模一样:都是更新normalizedSchema里的对应字段,调用addOrModifyById。我们可以把公共逻辑抽出来,避免重复代码。
方案一:抽离通用更新逻辑
先写一个通用的字段更新函数,然后在模式匹配里传递对应的字段操作回调:
type todo = {id: int, text: string}; type todoItem = {id: int, content: string}; type idFunction('a) = 'a => int; type recordType = [ | `Todo(todo, idFunction(todo)) | `TodoItem(todoItem, idFunction(todoItem)) ]; type normalizedSchema = { todo: list(todo), todoItem: list(todoItem), }; // 通用的字段更新逻辑 let updateField = (schema, item, idFun, getField, setField) => { let currentItems = getField(schema); let updatedItems = addOrModifyById(currentItems, item, idFun); setField(schema, updatedItems); }; let commitItemToSchema = (normalizedSchema, recordType) => { switch(recordType) { | `Todo(item, idFun) => updateField( normalizedSchema, item, idFun, schema => schema.todo, (schema, newTodos) => {...schema, todo: newTodos} ) | `TodoItem(item, idFun) => updateField( normalizedSchema, item, idFun, schema => schema.todoItem, (schema, newItems) => {...schema, todoItem: newItems} ) }; };
这里用getField和setField两个回调,抽象了“获取字段值”和“更新字段值”的操作,后续新增变体时,只需要传递对应的回调即可。
方案二:用模块系统实现可扩展的泛化
如果后续可能新增更多变体类型,用模块系统封装每个实体的逻辑会更优雅,扩展性更强:
// 定义实体模块的统一接口 module type Entity = { type t; // 从schema中获取当前实体列表 let get: normalizedSchema => list(t); // 将更新后的实体列表写入schema let set: (normalizedSchema, list(t)) => normalizedSchema; }; // Todo实体的具体实现 module Todo: Entity with type t = todo = { type t = todo; let get = schema => schema.todo; let set = (schema, newTodos) => {...schema, todo: newTodos}; }; // TodoItem实体的具体实现 module TodoItem: Entity with type t = todoItem = { type t = todoItem; let get = schema => schema.todoItem; let set = (schema, newItems) => {...schema, todoItem: newItems}; }; // 通用的实体更新函数,接收实体模块作为参数 let updateEntity = (module E: Entity with type t = 'a) => (schema: normalizedSchema, item: 'a, idFun: idFunction('a)) => { let currentItems = E.get(schema); let updatedItems = addOrModifyById(currentItems, item, idFun); E.set(schema, updatedItems); }; let commitItemToSchema = (normalizedSchema, recordType) => { switch(recordType) { | `Todo(item, idFun) => updateEntity(module Todo)(normalizedSchema, item, idFun) | `TodoItem(item, idFun) => updateEntity(module TodoItem)(normalizedSchema, item, idFun) }; };
这种方式下,新增一个变体类型时,只需要新增对应的实体模块,完全不需要修改commitItemToSchema的核心逻辑,非常适合需要频繁扩展的场景。
内容的提问来源于stack exchange,提问作者GTDev
相关产品推荐
相关产品推荐

