Eu tive essa pergunta muito enquanto aprendia e, desde então, já a fiz várias vezes. A maneira mais simples de descrever a diferença é que ambas são iguais :) Deixe-me explicar ... obviamente existem diferenças.
Tanto a aplicação parcial quanto o curry envolvem o fornecimento de argumentos para uma função, talvez não todos de uma vez. Um exemplo bastante canônico é adicionar dois números. No pseudocódigo (na verdade, JS sem palavras-chave), a função base pode ser a seguinte:
add = (x, y) => x + y
Se eu quisesse uma função "addOne", poderia aplicá-la parcialmente ou curry:
addOneC = curry(add, 1)
addOneP = partial(add, 1)
Agora, usá-los é claro:
addOneC(2) #=> 3
addOneP(2) #=> 3
Então qual a diferença? Bem, é sutil, mas a aplicação parcial envolve o fornecimento de alguns argumentos e a função retornada executará a função principal na próxima chamada, enquanto o currying continuará esperando até que tenha todos os argumentos necessários:
curriedAdd = curry(add) # notice, no args are provided
addOne = curriedAdd(1) # returns a function that can be used to provide the last argument
addOne(2) #=> returns 3, as we want
partialAdd = partial(add) # no args provided, but this still returns a function
addOne = partialAdd(1) # oops! can only use a partially applied function once, so now we're trying to add one to an undefined value (no second argument), and we get an error
Em resumo, use o aplicativo parcial para preencher alguns valores, sabendo que na próxima vez que você chamar o método, ele será executado, deixando indefinidos todos os argumentos não fornecidos; use currying quando desejar retornar continuamente uma função parcialmente aplicada quantas vezes for necessário para cumprir a assinatura da função. Um exemplo final artificial:
curriedAdd = curry(add)
curriedAdd()()()()()(1)(2) # ugly and dumb, but it works
partialAdd = partial(add)
partialAdd()()()()()(1)(2) # second invocation of those 7 calls fires it off with undefined parameters
Espero que isto ajude!
ATUALIZAÇÃO: Algumas implementações de linguagens ou libs permitem que você passe uma aridade (número total de argumentos na avaliação final) para a implementação parcial do aplicativo, que pode confundir minhas duas descrições em uma confusão confusa ... mas nesse momento, as duas técnicas são amplamente intercambiável.