malli 2025-02-26

I'm trying to refer to schemas directly in my schema:

(m/explain
    [:map
     [:function-name :string]
     [:description :string]
     [:signature :schema]]              ; I've also tried m/schema?

    {:function-name "add"
     :description   "Adds two (and only two) numbers"
     :signature     (m/schema [:=> [:cat :int :int] :int])}) ; also tried without the m/schema wrapping
The value of :signature should be the function schema, not a function conforming to the schema. This results in a rather cryptic:
{:type clojure.lang.ExceptionInfo,
   :message ":malli.core/child-error",
   :data
   {:type :malli.core/child-error,
    :message :malli.core/child-error,
    :data
    {:type :schema, :properties nil, :children nil, :min 1, :max 1}},
   :at [malli.core$_exception invokeStatic "core.cljc" 157]}]
Interestingly using [:signature m/schema?] results in an invalid schema error for some reason.

yeah :schema gives you a child error because it's used as a wrapper for things like recursive schemas internally, so always in the form [:schema <something else>]

m/schema? is the right thing to check for, but it doesn't work because malli has an explicit whitelist of predicate schemas

[:fn m/schema?] should work, however

user=> (m/explain
    [:map
     [:function-name :string]
     [:description :string]
     [:signature [:fn m/schema?]]]

    {:function-name "add"
     :description   "Adds two (and only two) numbers"
     :signature     (m/schema [:=> [:cat :int :int] :int])})
nil
user=> (m/explain
    [:map
     [:function-name :string]
     [:description :string]
     [:signature [:fn m/schema?]]]

    {:function-name "add"
     :description   "Adds two (and only two) numbers"
     :signature     false})
{:schema [:map [:function-name :string] [:description :string] [:signature [:fn #object[malli.core$schema_QMARK_ 0x7757a37f "malli.core$schema_QMARK_@7757a37f"]]]], :value {:function-name "add", :description "Adds two (and only two) numbers", :signature false}, :errors ({:path [:signature], :in [:signature], :schema [:fn #object[malli.core$schema_QMARK_ 0x7757a37f "malli.core$schema_QMARK_@7757a37f"]], :value false})}

Oh interesting thank you. I assumed I could use any predicate function as a predicate (like spec I guess)

This restriction in Malli is because one of the points of Malli was to make schemas that can be persisted in a db

so can't allow for arbitrary code, or for references to functions that might change between serialising and deserialising the schema

recently there's been a move away from functions-as-schemas to named schemas: :int instead of int? etc

just to give some context to you

I'm surprised there isn't a builtin named schema for [:fn m/schema?]

It seems like something that would crop up more

I'm thinking of using malli to generate mocks for database queries in testing. Is there any prior art out there I could use as reference?

I wouldn't directly mock the DB, but instead generate data and actually insert it in a rollback TX. I've been bitten by too many slight differences in behavior. I have used https://github.com/donut-party/datapotato for this and liked it overall, but it does have limitations & quirks

👀 1

using with-redefs or a test component, you can have the database query function return generated conforming values for your schemas

i'm on mobile or i'd write an example, but i think it's self explanatory