moonbit-phonetic

Deterministic phonetic encoding, explainable name matching, indexing, and deduplication for MoonBit

phonetic
name-matching
soundex
metaphone
deduplication
moon add zbhzs1/moonbit-phonetic@0.3.0
Download zip
Author
Version
0.3.0
License
Apache-2.0
Last updated
19 days ago
Downloads
8
README

#MoonBit Phonetic Encoding and Name Matching Toolkit

一个使用 MoonBit 实现的确定性拉丁姓名编码与匹配基础库。项目提供八种语音编码器、显式标准化策略、四种字符串相似度、可解释匹配、内存候选索引、结构化快照、批量去重和跨数据集一对一关联报告。所有匹配结果均来自公开 API,不包含网络或数据库桩实现。

#安装

moon add zbhzs1/moonbit-phonetic

在调用方的 moon.pkg 中导入:

import {
"zbhzs1/moonbit-phonetic" @phonetic,
}

#已实现能力

语音编码器:American Soundex、Refined Soundex、NYSIIS、Original Metaphone、Double Metaphone、Caverphone 1.0、Caverphone 2.0 和 Match Rating Approach。Double Metaphone 返回主键和不同的备用键;Caverphone 1.0 固定六字符,2.0 固定十字符。

字符串相似度:标准化 Levenshtein、Jaro、Jaro-Winkler 和保留重复 n-gram 计数的 Dice。Jaro-Winkler 缩放参数和 Dice n-gram 长度会被校验。

姓名匹配:match_names 返回标准化输入、每个编码器的键、各分量原始分数、权重、贡献、总分、阈值、决定和通知。提供 Conservative、Balanced 和 Recall 三套确定性默认配置;名称仅描述策略倾向,不代表测得的准确率。

索引与去重:NameIndex 支持插入、更新、删除、候选解释、稳定 top-K 查询、桶统计,以及带派生字段复算校验的结构化快照。deduplicate_names 对共享音标桶产生的无序候选对各比较一次,用确定性 union-find 形成传递分组,并返回接受边和汇总计数。

批量记录关联:link_name_datasets 对两个独立姓名数据集执行输入校验、音标分桶、候选上限控制和可解释评分,再按分数及字典序执行确定性贪心一对一选择。报告包含接受链接、双方未匹配 ID、可选的拒绝候选、阶段计数和算法说明;重复或空 ID、非法姓名和配置错误均通过类型化错误返回。

#最小用法

let config = @phonetic.balanced_match_config()
match @phonetic.match_names("John Smith", "Smith Jon", config) {
Ok(evidence) => println(evidence.matched)
Err(_) => println("invalid input or configuration")
}

let index = match @phonetic.NameIndex::new(config, [@phonetic.Soundex]) {
Ok(value) => value
Err(_) => abort("invalid index configuration")
}
ignore(index.insert("person-1", "Robert"))
ignore(index.insert("person-2", "Rupert"))
let result = index.query("Robert", 10, 0.0)

#可运行示例

moon run examples/basic moon run examples/matching moon run examples/deduplicate moon run examples/linkage

examples/matching 输出 SmithSchmidt 的三个分量、总分 0.8661904761904762、阈值 0.7matched=trueexamples/deduplicate 输出两个重复组、一个单例,以及 4 个候选、4 次比较和 3 条接受边。examples/linkage 输出两条跨数据集链接、双方各一个未匹配 ID,以及 2 个候选、2 次评估和 2 条接受链接。CI 会执行全部四个示例。

#标准化边界

旧版 soundexrefined_soundexnysiisencodeis_match 保留 0.1.0 的 ASCII 兼容行为。新 API 使用 NormalizationConfig:可分别处理大小写、空白、连字符、撇号、数字和普通标点,并对文档列出的拉丁字符执行显式折叠。未列出的非 ASCII 字母会返回 RejectedCharacter,项目不宣称完整 Unicode 或多语言姓名支持。

#本地验证

moon clean moon fmt --check moon info moon check moon build moon test moon run examples/basic moon run examples/matching moon run examples/deduplicate moon run examples/linkage moon publish --dry-run

#功能边界

本项目不处理音频、语音识别、中文拼音或通用多语言转写;不提供网络服务、数据库连接或文件序列化。索引快照是结构化 MoonBit 值,不等同于磁盘持久化。批量去重与跨数据集关联只评估共享配置音标键的候选,未进入候选集不代表已证明不相似;关联结果采用确定性贪心选择,不宣称全局最优。项目不提供真实发音相同、身份相同或生产准确率保证,也未实现 Metaphone 3。

算法规则、测试向量来源和许可证说明见 docs/references.md,实现与申报能力对应关系见 docs/development_report.md。项目采用 Apache License 2.0

pub(all) struct AcceptedLink {
left_id : String
right_id : String
evidence : MatchEvidence
} derive(Eq,
Debug
)

Represents one accepted one-to-one record link.

#
Algorithm

pub(all) enum Algorithm {
Soundex
RefinedSoundex
Nysiis
Metaphone
DoubleMetaphone
Caverphone1
Caverphone2
MatchRating
} derive(Eq,
Debug
)

Selects one of the package's supported phonetic encoders.

#
CasePolicy

pub(all) enum CasePolicy {
Preserve
Upper
Lower
} derive(Eq,
Debug
)

Controls ASCII case conversion after character normalization.

#
CharacterPolicy

pub(all) enum CharacterPolicy {
Keep
Drop
Reject
} derive(Eq,
Debug
)

Controls how a class of input characters is handled.

#
ComponentScore

pub(all) struct ComponentScore {
name : String
raw_score : Double
weight : Double
contribution : Double
} derive(Eq,
Debug
)

Records one raw and weighted score used in a matching decision.

#
DedupEdge

pub(all) struct DedupEdge {
left_id : String
right_id : String
evidence : MatchEvidence
} derive(Eq,
Debug
)

Retains the evidence for one accepted canonical record pair.

#
DedupError

pub(all) enum DedupError {
InvalidUnionFindSize(Int)
UnionFindIndexOutOfBounds(Int, Int)
DuplicateInputId(String)
DedupIndexFailed(IndexError)
DedupMatchFailed(MatchError)
} derive(Eq,
Debug
)

Reports invalid batch inputs or a failed matching/indexing stage.

#
DedupGroup

pub(all) struct DedupGroup {
representative : DedupInput
members : Array[DedupInput]
} derive(Eq,
Debug
)

Contains one connected duplicate group and its stable representative.

#
DedupInput

pub(all) struct DedupInput {
id : String
name : String
} derive(Eq,
Debug
)

Supplies one stable identifier and name to batch deduplication.

#
DedupReport

pub(all) struct DedupReport {
groups : Array[DedupGroup]
singletons : Array[DedupInput]
accepted_edges : Array[DedupEdge]
summary : DedupSummary
} derive(Eq,
Debug
)

Returns canonical groups, optional singletons, accepted evidence, and counts.

#
DedupSummary

pub(all) struct DedupSummary {
input_count : Int
candidate_pair_count : Int
comparison_count : Int
accepted_edge_count : Int
group_count : Int
ungrouped_count : Int
} derive(Eq,
Debug
)

Counts all observable stages of one deduplication run.

#
EncodedKeys

pub(all) struct EncodedKeys {
primary : String
alternate : String?
} derive(Eq,
Debug
)

Holds one primary phonetic key and an optional distinct alternate key.

#
EncodedKeys::all_non_empty_keys

fn EncodedKeys::all_non_empty_keys(self : EncodedKeys) -> Array[String]

Returns non-empty keys in primary then alternate order.

#
EncodedKeys::new

fn EncodedKeys::new(primary : String, alternate : String?) -> EncodedKeys

Creates keys while removing an empty or duplicate alternate.

#
EncodedKeys::single

fn EncodedKeys::single(primary : String) -> EncodedKeys

Creates an encoding with only one key.

#
EncoderWeight

pub(all) struct EncoderWeight {
algorithm : Algorithm
weight : Double
} derive(Eq,
Debug
)

Assigns a non-negative contribution weight to a phonetic encoder.

#
IndexCandidateRecord

pub(all) struct IndexCandidateRecord {
record : NameRecord
normalized_name : String
shared_bucket_keys : Array[String]
} derive(Eq,
Debug
)

Explains which blocking keys admitted one candidate.

#
IndexCandidateSet

pub(all) struct IndexCandidateSet {
query : String
normalized_query : String
query_bucket_keys : Array[String]
records : Array[IndexCandidateRecord]
} derive(Eq,
Debug
)

Exposes a deterministic candidate set before similarity scoring.

#
IndexError

pub(all) enum IndexError {
InvalidIndexMatchConfig(MatchError)
NoBlockingAlgorithms
DuplicateBlockingAlgorithm(Algorithm)
EmptyRecordId
DuplicateRecord(String)
MissingRecord(String)
IndexNormalizationFailed(NormalizationError)
NoBlockingKeys(String)
InvalidQueryLimit(Int)
InvalidMinimumScore(Double)
IndexMatchFailed(MatchError)
UnsupportedSnapshotVersion(Int)
DuplicateSnapshotRecord(String)
InconsistentSnapshotRecord(String)
} derive(Eq,
Debug
)

Reports invalid index setup, records, or normalized data.

#
IndexMatch

pub(all) struct IndexMatch {
record : NameRecord
normalized_name : String
evidence : MatchEvidence
} derive(Eq,
Debug
)

Associates one indexed record with its complete matching evidence.

#
IndexQueryResult

pub(all) struct IndexQueryResult {
query : String
normalized_query : String
matches : Array[IndexMatch]
summary : IndexQuerySummary
} derive(Eq,
Debug
)

Contains ranked query matches and auditable stage counts.

#
IndexQuerySummary

pub(all) struct IndexQuerySummary {
candidate_count : Int
scored_count : Int
returned_count : Int
} derive(Eq,
Debug
)

Counts the candidate, scoring, and result stages of an index query.

#
IndexSnapshot

pub(all) struct IndexSnapshot {
version : Int
config : MatchConfig
blocking_algorithms : Array[Algorithm]
records : Array[IndexSnapshotRecord]
} derive(Eq,
Debug
)

Represents a versioned, structured index export without file I/O claims.

#
IndexSnapshotRecord

pub(all) struct IndexSnapshotRecord {
record : NameRecord
normalized : String
bucket_keys : Array[String]
} derive(Eq,
Debug
)

Stores original and recomputable derived fields for one record.

#
IndexStatistics

pub(all) struct IndexStatistics {
record_count : Int
bucket_count : Int
key_assignment_count : Int
largest_bucket_size : Int
average_bucket_size : Double
} derive(Eq,
Debug
)

Summarizes current inverted-index occupancy.

#
LinkCandidate

pub(all) struct LinkCandidate {
left_id : String
right_id : String
score : Double
accepted : Bool
evidence : MatchEvidence
} derive(Eq,
Debug
)

Retains complete matching evidence for one blocked cross-dataset pair.

#
LinkRecord

pub(all) struct LinkRecord {
id : String
name : String
} derive(Eq,
Debug
)

Supplies one caller-defined identifier and Latin name to batch linkage.

#
LinkageConfig

pub(all) struct LinkageConfig {
match_config : MatchConfig
blocking_algorithms : Array[Algorithm]
max_candidates_per_left : Int
minimum_score : Double
retain_rejected : Bool
} derive(Eq,
Debug
)

Configures blocking, scoring, result retention, and per-record work limits.

#
LinkageError

pub(all) enum LinkageError {
InvalidLinkageMatchConfig(MatchError)
NoLinkageBlockingAlgorithms
DuplicateLinkageBlockingAlgorithm(Algorithm)
InvalidLinkageCandidateLimit(Int)
InvalidLinkageMinimumScore(Double)
EmptyLinkageRecordId(LinkageSide)
DuplicateLinkageRecordId(LinkageSide, String)
LinkageIndexFailed(LinkageSide, String, IndexError)
LinkageMatchFailed(String, String, MatchError)
} derive(Eq,
Debug
)

Reports invalid settings, records, or a failed indexing or matching stage.

#
LinkageReport

pub(all) struct LinkageReport {
links : Array[AcceptedLink]
unmatched_left_ids : Array[String]
unmatched_right_ids : Array[String]
rejected_candidates : Array[LinkCandidate]
stats : LinkageStats
notices : Array[String]
} derive(Eq,
Debug
)

Contains deterministic links, unmatched IDs, optional rejections, and counts.

#
LinkageSide

pub(all) enum LinkageSide {
LinkageLeft
LinkageRight
} derive(Eq,
Debug
)

Identifies the input side associated with a linkage validation error.

#
LinkageStats

pub(all) struct LinkageStats {
left_input_count : Int
right_input_count : Int
blocked_candidate_count : Int
evaluated_pair_count : Int
accepted_link_count : Int
rejected_candidate_count : Int
unmatched_left_count : Int
unmatched_right_count : Int
} derive(Eq,
Debug
)

Counts each observable stage of a batch linkage run.

#
MatchConfig

pub(all) struct MatchConfig {
normalization : NormalizationConfig
encoders : Array[EncoderWeight]
metric : SimilarityMetric
string_weight : Double
threshold : Double
token_policy : TokenPolicy
unmatched_token_penalty : Double
} derive(Eq,
Debug
)

Configures normalization, evidence components, and the decision boundary.

#
MatchConfig::with_encoders

fn MatchConfig::with_encoders(self : MatchConfig, encoders : Array[EncoderWeight]) -> MatchConfig

Returns a copy with a separate encoder array owned by the new config.

#
MatchConfig::with_metric

fn MatchConfig::with_metric(self : MatchConfig, metric : SimilarityMetric) -> MatchConfig

Returns a copy with a different string-similarity metric.

#
MatchConfig::with_normalization

fn MatchConfig::with_normalization(self : MatchConfig, normalization : NormalizationConfig) -> MatchConfig

Returns a copy with a different normalization policy.

#
MatchConfig::with_string_weight

fn MatchConfig::with_string_weight(self : MatchConfig, string_weight : Double) -> MatchConfig

Returns a copy with a different string component weight.

#
MatchConfig::with_threshold

fn MatchConfig::with_threshold(self : MatchConfig, threshold : Double) -> MatchConfig

Returns a copy with a different decision threshold.

#
MatchConfig::with_token_settings

fn MatchConfig::with_token_settings(self : MatchConfig, token_policy : TokenPolicy, unmatched_token_penalty : Double) -> MatchConfig

Returns a copy with different token assignment and penalty settings.

#
MatchError

pub(all) enum MatchError {
InvalidThreshold(Double)
InvalidEncoderWeight(Algorithm, Double)
InvalidStringWeight(Double)
NoPositiveComponents
DuplicateEncoder(Algorithm)
InvalidUnmatchedTokenPenalty(Double)
InvalidNormalizationConfig(NormalizationError)
InvalidSimilarityMetric(SimilarityError)
NormalizationFailed(NormalizationError)
SimilarityFailed(SimilarityError)
} derive(Eq,
Debug
)

Reports invalid matching configuration or a failed matching stage.

#
MatchEvidence

pub(all) struct MatchEvidence {
left_normalized : String
right_normalized : String
left_keys : Array[EncodedKeys]
right_keys : Array[EncodedKeys]
components : Array[ComponentScore]
score : Double
threshold : Double
matched : Bool
notices : Array[String]
} derive(Eq,
Debug
)

Contains the normalized values, keys, components, and final decision.

#
NameIndex

pub struct NameIndex {
config : MatchConfig
blocking_algorithms : Array[Algorithm]
records : Map[String, NameRecord]
normalized : Map[String, String]
record_bucket_keys : Map[String, Array[String]]
buckets : Map[String, Array[String]]
}

Maintains records and an in-memory inverted index of phonetic keys.

#
NameIndex::candidate_records

fn NameIndex::candidate_records(self : NameIndex, name : String) -> Result[IndexCandidateSet, IndexError]

Returns the unscored records admitted by shared phonetic blocking keys.

#
NameIndex::export_snapshot

fn NameIndex::export_snapshot(self : NameIndex) -> IndexSnapshot

Exports a canonical version-one snapshot sorted by record identifier.

#
NameIndex::from_snapshot

fn NameIndex::from_snapshot(snapshot : IndexSnapshot) -> Result[NameIndex, IndexError]

Rebuilds an index and verifies every supplied derived field by recomputation.

#
NameIndex::get

fn NameIndex::get(self : NameIndex, id : String) -> Result[NameRecord, IndexError]

Returns the original record for an identifier.

#
NameIndex::insert

fn NameIndex::insert(self : NameIndex, id : String, name : String) -> Result[Unit, IndexError]

Inserts one new record and its derived blocking keys.

#
NameIndex::length

fn NameIndex::length(self : NameIndex) -> Int

Returns the number of indexed records.

#
NameIndex::new

fn NameIndex::new(config : MatchConfig, blocking_algorithms : Array[Algorithm]) -> Result[NameIndex, IndexError]

Creates an empty index after validating matching and blocking settings.

#
NameIndex::query

fn NameIndex::query(self : NameIndex, name : String, limit : Int, minimum_score : Double) -> Result[IndexQueryResult, IndexError]

Retrieves blocking candidates, scores each once, and returns stable top-K results.

#
NameIndex::remove

fn NameIndex::remove(self : NameIndex, id : String) -> Result[Unit, IndexError]

Removes a record and deletes buckets that become empty.

#
NameIndex::statistics

fn NameIndex::statistics(self : NameIndex) -> IndexStatistics

Reports deterministic aggregate counts for the current in-memory index.

#
NameIndex::update

fn NameIndex::update(self : NameIndex, id : String, name : String) -> Result[Unit, IndexError]

Replaces a record name and atomically refreshes its derived keys.

#
NameRecord

pub(all) struct NameRecord {
id : String
name : String
} derive(Eq,
Debug
)

Stores the caller's stable identifier and original name.

#
NormalizationConfig

pub(all) struct NormalizationConfig {
case_policy : CasePolicy
punctuation : CharacterPolicy
whitespace : CharacterPolicy
hyphen : CharacterPolicy
apostrophe : CharacterPolicy
digits : CharacterPolicy
fold_latin_diacritics : Bool
max_input_length : Int
} derive(Eq,
Debug
)

Configures deterministic name normalization.

#
NormalizationConfig::with_case_policy

fn NormalizationConfig::with_case_policy(self : NormalizationConfig, case_policy : CasePolicy) -> NormalizationConfig

Returns a copy with a different case policy.

#
NormalizationConfig::with_character_policies

fn NormalizationConfig::with_character_policies(self : NormalizationConfig, punctuation : CharacterPolicy, whitespace : CharacterPolicy, hyphen : CharacterPolicy, apostrophe : CharacterPolicy, digits : CharacterPolicy) -> NormalizationConfig

Returns a copy with policies for punctuation, whitespace, hyphens, apostrophes, and digits in that order.

#
NormalizationConfig::with_max_input_length

fn NormalizationConfig::with_max_input_length(self : NormalizationConfig, max_input_length : Int) -> NormalizationConfig

Returns a copy with a different maximum input length.

#
NormalizationError

pub(all) enum NormalizationError {
InvalidMaximumLength(Int)
InputTooLong(Int, Int)
RejectedCharacter(Int, UInt16)
EmptyNormalizedInput
} derive(Eq,
Debug
)

Reports invalid configuration or unsupported input.

#
NormalizationNotice

pub(all) enum NormalizationNotice {
CharactersDropped(Int)
DiacriticsFolded(Int)
InputTruncated(Int)
} derive(Eq,
Debug
)

Describes a non-fatal transformation applied during normalization.

#
NormalizationResult

pub(all) struct NormalizationResult {
original : String
normalized : String
tokens : Array[String]
notices : Array[NormalizationNotice]
truncated : Bool
} derive(Eq,
Debug
)

Contains normalized text, tokens, and transformation evidence.

#
SimilarityError

pub(all) enum SimilarityError {
InvalidWinklerScaling(Double)
InvalidNGramSize(Int)
} derive(Eq,
Debug
)

Reports an invalid metric parameter.

#
SimilarityMetric

pub(all) enum SimilarityMetric {
Levenshtein
Jaro
JaroWinkler(Double)
Dice(Int)
} derive(Eq,
Debug
)

Selects a deterministic string-similarity metric.

#
TokenPolicy

pub(all) enum TokenPolicy {
WholeInput
BestTokenPairs
} derive(Eq,
Debug
)

Selects whole-input or token-aware matching.

#
balanced_match_config

fn balanced_match_config() -> MatchConfig

Returns the default mixed phonetic and token-aware strategy.

#
caverphone1

fn caverphone1(input : String) -> String

Encodes ASCII letters with the six-character Caverphone 1.0 rules.

#
caverphone2

fn caverphone2(input : String) -> String

Encodes ASCII letters with the ten-character Caverphone 2.0 rules.

#
conservative_match_config

fn conservative_match_config() -> MatchConfig

Returns a high-threshold whole-name strategy.

#
conservative_normalization_config

fn conservative_normalization_config() -> NormalizationConfig

Returns a strict configuration that rejects potentially lossy input.

#
deduplicate_names

fn deduplicate_names(inputs : Array[DedupInput], config : MatchConfig, blocking_algorithms : Array[Algorithm], include_singletons : Bool) -> Result[DedupReport, DedupError]

Groups records connected by accepted, phonetic-blocked matching edges.

#
dice_similarity

fn dice_similarity(left : String, right : String, size : Int) -> Result[Double, SimilarityError]

Computes multiset Dice similarity for character n-grams.

#
double_metaphone

fn double_metaphone(input : String) -> EncodedKeys

Encodes a Latin-script name into standard four-character primary and alternate Double Metaphone keys.

#
encode

fn encode(input : String, algorithm : Algorithm) -> String

Encodes an input with the selected algorithm.

#
encode_all

fn encode_all(inputs : Array[String], algorithm : Algorithm) -> Array[String]

Encodes every input in order with the selected algorithm.

#
encode_keys

fn encode_keys(input : String, algorithm : Algorithm, normalization : NormalizationConfig) -> Result[EncodedKeys, NormalizationError]

Normalizes input with an explicit policy and returns all keys produced by the selected encoder.

#
is_match

fn is_match(left : String, right : String, algorithm : Algorithm) -> Bool

Compares non-empty phonetic keys produced by the selected algorithm.

#
jaro_similarity

fn jaro_similarity(left : String, right : String) -> Double

Computes Jaro similarity in the closed interval [0, 1].

#
jaro_winkler_similarity

fn jaro_winkler_similarity(left : String, right : String, scaling : Double) -> Result[Double, SimilarityError]

Computes Jaro-Winkler similarity with a validated prefix scaling factor.

#
join_tokens

fn join_tokens(tokens : Array[String]) -> String

Joins tokens with one ASCII space.

#
levenshtein_distance

fn levenshtein_distance(left : String, right : String) -> Int

Computes edit distance using two rows and MoonBit string index units.

#
levenshtein_similarity

fn levenshtein_similarity(left : String, right : String) -> Double

Returns 1 - distance / max_length in the closed interval [0, 1].
fn link_name_datasets(left : Array[LinkRecord], right : Array[LinkRecord], config : LinkageConfig) -> Result[LinkageReport, LinkageError]

Links two independent Latin-name datasets with deterministic greedy assignment.

Candidate generation is limited by shared phonetic blocking keys and the configured per-left evaluation cap. A returned link records only that the configured deterministic matcher accepted the pair; it is not proof of real-world identity. Assignment favors higher scores, then lexical IDs, and does not claim a globally optimal bipartite solution.

#
match_names

fn match_names(left : String, right : String, config : MatchConfig) -> Result[MatchEvidence, MatchError]

Compares two names and returns every score used by the final decision.

#
match_rating_codex

fn match_rating_codex(input : String) -> String

Generates the at-most-six-character Match Rating codex.

#
match_rating_compare

fn match_rating_compare(left : String, right : String) -> Bool

Compares two names with the Match Rating Approach decision procedure.

#
metaphone

fn metaphone(input : String) -> String

Encodes ASCII letters with the original Metaphone rule families.

#
name_normalization_config

fn name_normalization_config() -> NormalizationConfig

Returns the default configuration for Latin-script personal names.

#
normalize_name

fn normalize_name(input : String, config : NormalizationConfig) -> Result[NormalizationResult, NormalizationError]

Normalizes a Latin-script name according to an explicit policy.

#
nysiis

fn nysiis(input : String) -> String

Encodes ASCII letters with the untruncated NYSIIS rules.

#
recall_match_config

fn recall_match_config() -> MatchConfig

Returns a lower-threshold strategy intended to retain more candidates.

#
refined_soundex

fn refined_soundex(input : String) -> String

Encodes ASCII letters with the US English Refined Soundex mapping.

#
soundex

fn soundex(input : String) -> String

Encodes ASCII letters with the four-character American Soundex scheme.

#
string_similarity

fn string_similarity(left : String, right : String, metric : SimilarityMetric) -> Result[Double, SimilarityError]

Computes a score with the selected validated similarity metric.

#
tokenize_normalized

fn tokenize_normalized(input : String) -> Array[String]

Splits text on ASCII whitespace and ignores repeated separators.

#
validate_linkage_config

fn validate_linkage_config(config : LinkageConfig) -> Result[Unit, LinkageError]

Validates nested matching, blocking, work-limit, and score settings.

#
validate_match_config

fn validate_match_config(config : MatchConfig) -> Result[Unit, MatchError]

Validates every nested policy and score parameter before matching.

#
validate_normalization_config

fn validate_normalization_config(config : NormalizationConfig) -> Result[Unit, NormalizationError]

Validates limits that affect normalization resource use.