24 dias de Hackage, 2015 - dia 16 - safe; o que é segurança mesmo?
por Franklin Chen
traduzido por Pedro Yamada
Esse é um artigo escrito por Franklin Chen e traduzido para o português. Ler original.
Encontro HaskellBR São Paulo
Índice de toda a série
O índice de toda a série está no topo do artigo para o dia 1.
Dia 16
(Discussão no Reddit do original)
Hoje farei algo estranho: até agora, discuti bibliotecas e ferramentas que eu
de fato uso. Hoje, vou discutir uma biblioteca que não uso e pensar um
pouco sobre porque não a uso e porque poderia querer a usar. Essa biblioteca é
o safe
, que tenta nos proteger da
infeliz “insegurança” de funções comuns do Prelude
padrão.
Isso é uma coisa boa, certo? Afinal, no
dia 7,
eu promovi o uso da lista NonEmpty
e de novo a usei no
dia 14.
Eu gosto de segurança, mas o que é “segurança” mesmo?
Segurança é relativa
A noção de segurança é sempre relativa a algum critério, alguma expectativa e, de forma mais geral, no contexto de algum estilo de vida. E estilos de vida sempre podem ser protegidos por mecânismos diferentes, desde a proteção estríta até guias de estilo ou contratos sociais implícitos e ostracismo.
No contexto do pacote safe
, o tipo de segurança sobre o qual estamos
preocupados é nunca ver um programa quebrar com uma exceção vinda de código
puro como:
Isso acontece se você chama head
em uma lista vazia:
A solução segura é nunca chame head
em uma lista que pode estar vazia. Há
formas diferentes de garantir isso.
Resolvendo um problema da psicologia humana?
Uma solução é mudar o tipo de head
.
Podemos dizer que funções como head
no Prelude do Haskell são um erro
histórico de 1990 que continou na linguagem, porque ela encoraja programadores
(principalmente recem-chegados) a chamar head
. Se for fácil fazer coisas
inseguras, as pessoas vão certamente as fazer e provavelmente as fazer
frequentemente.
Comunidades de linguagens mais novas atacam o problema psicológico não
fornecendo uma função insegura head
; por exemplo, o ecossistema padrão do
PureScript fornece um
seguro Data.List.head
com tipo forall a. List a -> Maybe a
, mas também fornece um módulo
Data.List.Unsafe
que incluí
Data.List.Unsafe.head
com tipo forall a. List a -> a
e mais.
E o
módulo List
do Elm fornece
List.head
com tipo List a -> Maybe a
.
Marcando algo como inseguro pelo menos permite ao escritor e leitor do código a tomarem nota de que algo pode dar errado, então eu acho que esse é um bom começo para uma solução. Além disso, a pesquisa de psicologia tem mostrado claramente que padrões importam: se o objetivo é promover segurança, é melhor que o padrão seja seguro, e desabilitar ele explicitamente para ser inseguro, ao invés do contrário.
Infelizmente, por razões históricas, se você está trabalhando com listas ou algumas outras estruturas de dados em Haskell, você é forçado a ter que escolher explicitamente ser seguro, já que o padrão é inseguro.
O pacote safe
nos deixa ser seguros de forma mais fácil.
Você ganha funções como
Safe.headMay
com tipo [a] -> Maybe a
.
Uma alternativa: fazendo pattern-matching diretamente na estrutura de dados
Na prática, eu não uso funções como headMay
, porque eu só faço
pattern-matching na própria lista:
Quando eu penso a respeito, no entanto, há algo que cheira mal nessa solução. O
wildcard _
no pattern mostra que nós estamos extraindo mais informação do que
precisamos de fato. A princípio, nós poderíamos só extrair o que precisamos e
headMay
faz exatamente isso. Eu estou basicamente violando o encapsulamento
conceitual extraindo e ignorando mais do que eu preciso (o resto da lista).
Então acredito que eu de fato deveria começar a usar o headMay
nesse tipo de
contexto e pensando mais profundamente, acho que o único motivo que esse ainda
não é o caso é que o Prelude padrão não o incluí! Era mais fácil fazer
pattern-matching do que encontrar o safe
e o adicionar como uma
dependência.
Quantos de vocês pensam como eu e usariam o headMay
se ele fizesse parte do
Prelude, mas já que ele não faz, usam um pattern match na lista?
Revisitando a solução headMay
Alguns de vocês podem gostar de usar a função maybe
que tem tipo b -> (a ->
b) -> Maybe a -> b
, para evitar fazer pattern-matching no Maybe
:
Também há uma versão “code-golf” que eu não recomendo:
Outras boas coisas no safe
Cada uma das funções ...May
também tem outras variações úteis.
Uma delas permite especificar um padrão para retornar no caso de que lista esteja vazia:
Outra é insegura, mas pelo menos gera uma exceção melhor. Isso é útil se você sabe que uma lista não está vazia, e você não quer ter que lidar com o caso em que ela está vazia, mas por via das dúvidas, gerar uma exceção que se atirada, pelo menos te diz de onde o seu “erro interno fatal” veio.
Ser exato é um problema de segurança
O módulo
Safe.Exact
fornece uma série de funções úteis que têm a ver com acessar elementos
arbitrários de uma lista e checar o tamanho de listas. Aqui, “segurança” não
mais se refere a uma exceção. Ela se refere a algo mais sútil: o código que
você escreve que dá type-check e roda, mas faz algo inesperado. Por exemplo,
é fácil usar o take
de alguma forma que você não queira, porque ele permite
silenciosamente que você “pegue” mais elementos da lista do que ela contém,
mas só assume que sabe o que está fazendo e não quer dizer “pegue 1000
elementos” mas “pegue 1000 elementos e se não tiverem 1000 elementos, pegue
todos os elementos”. Eu já fui mordido pelo take
antes, em uma ocasião em que
passei um número absurdo que eu não queria. Então a família de funções
Safe.Exact.takeExact...
é muito útil. No passado, antes de descobrir sobre o
safe
, eu basicamente escrevia meus próprios wrappers e agora não vou ter mais
que fazer isso.
Foldable
Finalmente o módulo
Safe.Foldable
é útil porque o Foldable
é cheio de operações inseguras.
Quando você sabe alguma coisa sobre seus dados que o tipo não sabe
Uma nota final sobre tratar a ideia de ser exato em operações sobre listas como um problema de segurança: a solução embasada à não ser exato quando se trata de acessar um elemento ou o tamanho de listas é transformar os bugs em potencial em erros de tipo, usando tipos dependentes; um tipo de lista que é dependente no seu tamanho. Um dia de Hackage posterior vai mostrar soluções.
Conclusão
O pacote safe
é uma boa biblioteca utilitária que amarra as operações
inseguras do Prelude. Há razões técnicas e psicológias pelas quais eu ainda não
a usei, e eu discuti elas, mas vou a usar no futuro quando ela se encaixar nas
minhas necessidades.
Todo o código
Todo o código para a série estará nesse repositório do GitHub.
Nota do tradutor
Se você quer ajudar com esse tipo de coisa, agora é a hora. Entre no
Slack ou no
IRC da HaskellBR e
contribua. Esse blog e outros projetos associados estão na
organização haskellbr
no GitHub e em
haskellbr.com/git.