в случае АДТ ничего особенного не происходит с типами, у тебя просто появляются в контексте два новых имени
если ты пишешь case Bar(a) => в этом случае происходит то, что в конпеляторах называется унификация, т.е. компилятор узнает, что B = String , унифицирует два типа
и использует это в других своих выводах
если ты пишешь
case Bar(c, f) => в этом случае у тебя происходит другой эффект - ты знаешь, что во время конструкции был какой-то тип, и этот тип важен, чтобы расставить типы для с и f
например, ты должен знать что f можно применить к с а инту - без доп информации нельзя
т.е. конструктор можно представить как ∃C . c : C, f: C => (A, 😎
такие типы называются экзистенциальными, и во время патерн-матчинга конпелятор делает другой трюк - это притворяется, что этот блок находится внутри какой-то генерик\параметрического контекста, где есть абстрактный тип С, т.е. как будто бы ты объявил
def handleBar[C](c: C, f: C => (A, B)) = ... с телом твоего матча
такой процесс выворачивания экзистенциальных типов в универсальные определения называется сколемизацией