A questão principal aqui é que o encapsulamento não é um conceito rigidamente definido nem por que é útil. Fazer uma pesquisa mostra que a maneira como as pessoas veem o encapsulamento é altamente opinativa e que muitas pessoas o confundem com Abstração.
A primeira definição que você vai encontrar é
Encapsulamento é um conceito que une os dados e funções que manipulam os dados ...
Se essa é sua definição, a maioria das linguagens tem como agrupar dados e funções que operam nesses dados em classes, módulos, bibliotecas, espaços para nome, etc.
Mas eu argumentaria que esse não é o principal objetivo do encapsulamento, pois essa definição continua:
... e isso mantém os dois protegidos contra interferências externas e uso indevido.
A Wikipedia também concorda com isso:
Um mecanismo de linguagem para restringir o acesso direto a alguns dos componentes do objeto.
Mas agora, precisamos perguntar o que se entende por "interferência e uso indevido" e por que o acesso direto aos dados deve ser restrito. Eu acredito que há duas razões.
Primeiro, o escopo limitador no qual os dados podem ser mutados é do melhor interesse do desenvolvedor. Com muita frequência, é necessário ter lógica antes / depois do valor ser definido. E ter apenas um número limitado de locais onde o valor pode ser definido é extremamente valioso. Nos idiomas OOP, isso pode ser feito usando classes. Em linguagens funcionais "mutáveis", os fechamentos servem ao mesmo propósito. E porque sabemos que classes = fechamentos , é até discutível que linguagens funcionais mutáveis sejam um "paradigma" diferente do OOP.
Mas e as línguas imutáveis? Não tem problema de alterar variáveis. É aqui que entra a segunda questão: funções e dados de ligação permitem manter esses dados em um estado válido. Imagine que você tem uma estrutura imutável Email. Essa estrutura possui um único stringcampo. Temos requisitos para que, se você tiver um valor do tipo Email, esse campo contenha um endereço válido. No encapsulamento da OOP, isso é facilmente feito declarando esse campo private, fornecendo apenas Getmétodo e tendoconstructor methodque só será bem-sucedido se passado na cadeia de caracteres for um endereço válido. Coisa semelhante com fechamentos. Agora, para uma linguagem imutável, seria necessário dizer que a estrutura só pode ser inicializada através de uma função específica e essa função pode falhar. E não conheço nenhum idioma que se encaixe nesse critério (talvez alguém nos comentários possa me esclarecer).
Última edição é o que se entende por encapsulamento de "suporte" da linguagem. Por um lado, existem idiomas que permitem impor o encapsulamento, para que o código não seja compilado se o encapsulamento estiver quebrado. Por outro lado, o idioma pode fornecer uma maneira de encapsular, mas não o aplica, deixando o desenvolvedor aplicá-lo. Para o segundo caso, qualquer linguagem que possua estruturas e funções pode funcionar. Linguagens dinâmicas e Haskell vêm à mente. E eu diria que não há muitas línguas que caem do outro lado do espectro. Mesmo o C #, que é realmente bom em impor o encapsulamento para seus objetos, pode ser ignorado usando reflexão. Mas, vendo que em C # seria considerado um cheiro de código maciço e nenhum desenvolvedor de C # sano faria isso de bom grado.