A
arktype3mo ago
Dimava

Pipe chains don't work in rc13

I'm trying to make a logging wrapper to help me debug my Types but pipes don't seem to chain
function logWrapper<T extends type.Any>(T: T): T {
return (T.in as type.Any).pipe(
(v: any) => { console.log({ before: v }); return v },
T,
(v: any) => { console.log({ after: v }); return v },
) as any
}

const result = logWrapper(type('string.numeric.parse'))('1')
console.log({ result })
// logs { before: "1" } { after: "1" } { result: 1 }
function logWrapper<T extends type.Any>(T: T): T {
return (T.in as type.Any).pipe(
(v: any) => { console.log({ before: v }); return v },
T,
(v: any) => { console.log({ after: v }); return v },
) as any
}

const result = logWrapper(type('string.numeric.parse'))('1')
console.log({ result })
// logs { before: "1" } { after: "1" } { result: 1 }
18 Replies
Dimava
DimavaOP3mo ago
@ArkDavid
it("many Type pipes", () => {
const appendLength = type("string", "=>", (s) => `${s}${s.length}`)
const pipeNumbers = type("string").pipe(
appendLength,
appendLength,
s => `${s},`,
appendLength,
appendLength,
)
attest(pipeNumbers("a")).equals("a12,45")
})
it("many Type pipes", () => {
const appendLength = type("string", "=>", (s) => `${s}${s.length}`)
const pipeNumbers = type("string").pipe(
appendLength,
appendLength,
s => `${s},`,
appendLength,
appendLength,
)
attest(pipeNumbers("a")).equals("a12,45")
})
ssalbdivad
ssalbdivad3mo ago
What version of this? I fixed something similar in the last release
Dimava
DimavaOP3mo ago
It says 13 How can I know version in runtime? Is there type._arktypeVersion or whatever? Serious question Would use it all the bugs Anyways add this test please
Dimava
DimavaOP3mo ago
Nah it fails on rc14
No description
Dimava
DimavaOP3mo ago
Please ping me when you fix this, I think I'll pull the fix
ssalbdivad
ssalbdivad3mo ago
Since this works:
const pipeNumbers = type("string").pipe(
s => `${s}${s.length}`,
s => `${s}${s.length}`,
s => `${s},`,
s => `${s}${s.length}`,
s => `${s}${s.length}`
)
attest(pipeNumbers("a")).equals("a12,45")
const pipeNumbers = type("string").pipe(
s => `${s}${s.length}`,
s => `${s}${s.length}`,
s => `${s},`,
s => `${s}${s.length}`,
s => `${s}${s.length}`
)
attest(pipeNumbers("a")).equals("a12,45")
It looks like an incorrect optimization where we notice that last morph and the next pipe are === and ignore it or something May also have to do with the input being validated over and over I guess Yeah I guess it is the validated input This works:
const appendLength = (s: string) => `${s}${s.length}`
const pipeNumbers = type("string").pipe(
appendLength,
appendLength,
s => `${s},`,
appendLength,
appendLength
)
attest(pipeNumbers("a")).equals("a12,45")
const appendLength = (s: string) => `${s}${s.length}`
const pipeNumbers = type("string").pipe(
appendLength,
appendLength,
s => `${s},`,
appendLength,
appendLength
)
attest(pipeNumbers("a")).equals("a12,45")
Dimava
DimavaOP3mo ago
import { type } from 'arktype'

const appendLength = (s: string) => `${s}${s.length}`
const appendLengthT = type('string', '=>', appendLength)

const pipeNumbers = type([
type('string').pipe(appendLength, appendLength),
type('string').pipe(appendLength, appendLengthT),
type('string').pipe(appendLengthT, appendLength),
type('string').pipe(appendLengthT, appendLengthT),

type('string').pipe(appendLength, appendLength, appendLength),
type('string').pipe(appendLength, appendLength, appendLengthT),
type('string').pipe(appendLength, appendLengthT, appendLength),
type('string').pipe(appendLength, appendLengthT, appendLengthT),
type('string').pipe(appendLengthT, appendLength, appendLength),
type('string').pipe(appendLengthT, appendLength, appendLengthT),
type('string').pipe(appendLengthT, appendLengthT, appendLength),
type('string').pipe(appendLengthT, appendLengthT, appendLengthT),
])
const v = pipeNumbers.from(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'])
console.log(v)
import { type } from 'arktype'

const appendLength = (s: string) => `${s}${s.length}`
const appendLengthT = type('string', '=>', appendLength)

const pipeNumbers = type([
type('string').pipe(appendLength, appendLength),
type('string').pipe(appendLength, appendLengthT),
type('string').pipe(appendLengthT, appendLength),
type('string').pipe(appendLengthT, appendLengthT),

type('string').pipe(appendLength, appendLength, appendLength),
type('string').pipe(appendLength, appendLength, appendLengthT),
type('string').pipe(appendLength, appendLengthT, appendLength),
type('string').pipe(appendLength, appendLengthT, appendLengthT),
type('string').pipe(appendLengthT, appendLength, appendLength),
type('string').pipe(appendLengthT, appendLength, appendLengthT),
type('string').pipe(appendLengthT, appendLengthT, appendLength),
type('string').pipe(appendLengthT, appendLengthT, appendLengthT),
])
const v = pipeNumbers.from(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'])
console.log(v)
[
'a12', 'b1', 'c12', 'd1',
'e123', 'f123', 'g12', 'h1',
'i123', 'j123', 'k12', 'l1'
]
[
'a12', 'b1', 'c12', 'd1',
'e123', 'f123', 'g12', 'h1',
'i123', 'j123', 'k12', 'l1'
]
ssalbdivad
ssalbdivad3mo ago
Yeah I'm on it like I said it has to do with piping to another node incorrectly ignoring repeated pipes
Dimava
DimavaOP3mo ago
Hmm, interesting, only repeated ones? Okay now I understand nothing atall anyways, my LogWrapper doesn't work so (╯°□°)╯︵ ┻━┻
ssalbdivad
ssalbdivad3mo ago
I think so, like I said it has to do with normalization because there's a bunch of logic so that e.g. the second to can be ignored in type("string.json.parse").to({name: "string"}).to("object") I'll fix this
Dimava
DimavaOP3mo ago
But with (v) => T(v) it does work so ┬─┬ノ( º _ ºノ)
ssalbdivad
ssalbdivad3mo ago
There's a lot of complex logic around pipes and tons of edge cases hopefully at some point I will cover all these That's because AT doesn't understand what that does It's just like any other function Whereas if it's another type, we can reason about it and reduce things
Dimava
DimavaOP3mo ago
Yeh lol I think you don't even have a test for multiple pipe args
ssalbdivad
ssalbdivad3mo ago
Shouldn't matter really as long as it' s right for the base case Internally there is just a pipeOnce function that gets reduced
Dimava
DimavaOP3mo ago
The base case with multiple arguments should be checked anyways
ssalbdivad
ssalbdivad3mo ago
But there are still a lot of edge cases with morph nodes, unions, metadata etc.; These are the cases I'm adding + fixing (both broken now both in terms of structure and result):
const appendLengthMorph = (s: string) => `${s}${s.length}`

// https://discord.com/channels/957797212103016458/1291014543635517542
it("repeated Type pipe", () => {
const appendLength = type("string", "=>", appendLengthMorph)
const appendLengths = type("string").pipe(appendLength, appendLength)

attest(appendLengths.json).snap({
in: "string",
morphs: [
{ in: "string", morphs: ["$ark.appendLengthMorph"] },
{ in: "string", morphs: ["$ark.appendLengthMorph"] }
]
})

attest(appendLengths("a")).snap("a12")
})

// https://discord.com/channels/957797212103016458/1291014543635517542
it("repeated Type pipe with intermediate morph", () => {
const appendLength = type("string", "=>", appendLengthMorph)

const appendSeparatorMorph = (s: string) => `${s}|`

const appendSeparatedLengths = type("string").pipe(
appendLength,
appendLength,
appendSeparatorMorph,
appendLength,
appendLength
)

attest(appendSeparatedLengths.json).snap({
in: "string",
morphs: [
{ in: "string", morphs: ["$ark.appendLengthMorph"] },
{ in: "string", morphs: ["$ark.appendLengthMorph"] },
"$ark.appendSeparatorMorph",
{ in: "string", morphs: ["$ark.appendLengthMorph"] },
{ in: "string", morphs: ["$ark.appendLengthMorph"] }
]
})

attest(appendSeparatedLengths("a")).snap("a12|45")
})
const appendLengthMorph = (s: string) => `${s}${s.length}`

// https://discord.com/channels/957797212103016458/1291014543635517542
it("repeated Type pipe", () => {
const appendLength = type("string", "=>", appendLengthMorph)
const appendLengths = type("string").pipe(appendLength, appendLength)

attest(appendLengths.json).snap({
in: "string",
morphs: [
{ in: "string", morphs: ["$ark.appendLengthMorph"] },
{ in: "string", morphs: ["$ark.appendLengthMorph"] }
]
})

attest(appendLengths("a")).snap("a12")
})

// https://discord.com/channels/957797212103016458/1291014543635517542
it("repeated Type pipe with intermediate morph", () => {
const appendLength = type("string", "=>", appendLengthMorph)

const appendSeparatorMorph = (s: string) => `${s}|`

const appendSeparatedLengths = type("string").pipe(
appendLength,
appendLength,
appendSeparatorMorph,
appendLength,
appendLength
)

attest(appendSeparatedLengths.json).snap({
in: "string",
morphs: [
{ in: "string", morphs: ["$ark.appendLengthMorph"] },
{ in: "string", morphs: ["$ark.appendLengthMorph"] },
"$ark.appendSeparatorMorph",
{ in: "string", morphs: ["$ark.appendLengthMorph"] },
{ in: "string", morphs: ["$ark.appendLengthMorph"] }
]
})

attest(appendSeparatedLengths("a")).snap("a12|45")
})
Dimava
DimavaOP3mo ago
You shouldn't test as it's a whitebox
ssalbdivad
ssalbdivad3mo ago
Thanks for the 101 lol There's a reason everything is tested through the top-level type API But you have to reason to some extent about what logic actually needs to be tested E.g. if you are a library that wraps arktype, not testing the entire parser requires the white box knowledge that you didn't implement it These are fixed in 2.0.0-rc.14
Want results from more Discord servers?
Add your server